20200601のvue.jsに関する記事は19件です。

stateを直接参照しないシンプルなVuexサイクル

Vuexとは?

Vue.jsアプリケーションにおける状態管理パターンライブラリです。

コンポーネント間のデータや関数の受け渡しには、propsやemitを使いますが、Vuexは全てのコンポーネントのための集中型のStoreになります。
propsやemitだけでデータの受け渡しをするのは、バケツリレーに似ていて、vueファイルに何度も同じコードを書く必要があったり、無駄にロジックが発生します。

Vueコンポーネント層とVuexはどう繋がるのか?

今回の記事のタイトルは「stateを直接参照しないシンプルなVuexサイクル」です。
Vueコンポーネント層からStore内のstateにアクセスするためには

  • store.statemapStateを使ってstateを直接する方法
  • store.gettersmapGettersを用いて、取得する方法

があります。

ただ、Stateの変更をどこからでも直接、自由に変更、取得可能だと、全コンポーネント共通で参照可能なStateの秩序を保つことが出来ません。
そのため、公式ドキュメントにも掲載されている以下の図の通り、VuexのライフサイクルをルールとしてStateを変更、取得します。

vuex.png

stateの変更はMutationsが行い、vueコンポーネント層からはgetterを使用してStateの値を取得します。
そのMutaitionsはVueコンポーネント層から呼ばれるActionsによって実行されます

話がややこしくなってきたのでもう少し詳細にしてみます。
Vue.jsやNuxt.jsとVuexを使ってStateを管理したい。
そんなときに登場してくるのは Actions Mutaitions State Getters この4つです。

Vuex それぞれの役割

今回は、Firestoreに保存されているItemsを取得し、それをVuexのStateに格納して、コンポーネント層で使用する例を挙げます。
store/items以下に
- state.js
- action.js
- mutaition.js
- getter.js

を準備します。

State

Stateでは初期値を格納します。
基本的には配列が入る想定であれば[]、文字列を想定すれば''を初期値にします。
state.jsを用意して以下のように記述すると

export const state = () => {
  allItems: []
} 

stateの初期値はから配列になります。

Getters

GettersではStateの値をコンポーネント層で取得する際に使います。
例えば、Stateに格納されているallItemsを取得するなら以下のようにして

export const getters = {
  getAllItems: state => state. allItems
}

vueコンポーネントからは

computed: {
  allItems() {
    return this.$store.getters['items/getAllItems']
  }
}

のようにして取得できます。

ただこのままだと、allItemsは空配列なのでMutaitionsからStateを更新します。

Mutations

Actionsを定義する前に、Mutationsを用意します。

export const mutations = () => {
  setAllItems(state, items) {
    state.allItems = items
  }
}

setAllItemsという関数を作ります。
第1引数は値を変更するstateで、第2引数はActionsからもらうitemsとなります。
そのitemsをStateのallItemsの代入することでStateを更新できます。

Actions

次にVueコンポーネント層からActionsを呼ぶ必要があるので定義します。

export const actions = () => {
  async fetch({ commit }) {
    const docs = await Firestore.collection('items').get()
    const items = docs.map(doc => doc.data())
    commit('setAllItems', items)
  },
}

commit関数の第1引数にMutaitionsの実行したい関数を文字列で指定し、第2引数にFirestoreから取得したitemsを指定します。

コンポーネント層でからは以下のようにActionsを呼ぶことが出来ます。

fetch({store}) {
  store.dispatch('items/fetchAllItems')
}

所感

今回はState Getters Mutations Actionsの順で実装しましたが、どこから実装するのかに問題はありません。
名称からどんな役割があってどこからどんなふうに呼び出せば良いのか覚えるまでに時間がかかるかもしれませんが、使っていくうちに分かってきます。

ちなみにmentaというコーチングサービスでVue.jsを中心にメンターをしています。
もう少し詳しい内容を聞きたい方や、Vue.jsの勉強に行き詰まっている方など、ぜひmentawatsuyo_2にメッセージをしてください!

参考

Vuex とは何か?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsで画像をドラッグ&ドロップでアップロードする枠外にドロップした際、画像を表示させない方法。

ドラック&ドロップで間違って枠外にドロップしてしまうことがあります。
そうなると画像が表示されてしまい良いアップローダーとは、言えません。

以下を記述するだけで避けれます。

mounted () {
        window.ondrop = function(e){
            e.preventDefault();
        };
        window.ondragover = function(e){
            e.preventDefault();
        };
    }

参考になったら幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue/CSSアニメーション】BootstrapVueのダサすぎるCardコンポーネントをstyle修正で100億倍クールにしてみた(コピペOK)

スクリーンショット 2020-05-30 3.10.23.png

Vueバージョン確認

npm list vue

まずは上記コマンドでバージョンの確認

twinzlabo@0.1.0 /Users/twinzlabo

── vue@2.6.11

BootstrapVueの導入

BootstrapVueの導入がまだの方のために念のため導入方法書いときますね

とりあえずコピペして環境を整えてください

main.js
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue)
npm install vue bootstrap-vue bootstrap

以上でBootstrapVueの導入は完了です

Cardコンポーネントの白黒画像にhoverすると色がつくCSSアニメーション実装

すでに上の方で確認してもらったかと思いますが、

BootstrapVueのあまりしっくりこないカードコンポーネントをスタイル修正を行うことで

クールなデザインに編集していきましょう

スクリーンショット 2020-05-30 3.10.23.png

デフォルトの上の画像をhoverしたら下の画像のように色がつくようにカスタマイズしていきます

スクリーンショット 2020-05-30 3.10.30.png

この感じなかなかクールですよね

では早速コードをコピペしていきましょう

<template>
  <div>
    <b-card no-body class="overflow-hidden" style="max-width: 540px;margin: 0 auto">
      <b-row no-gutters>
        <b-col md="6">
          <b-card-img src="https://picsum.photos/400/400/?image=20" alt="Image" class="rounded-0"></b-card-img>
        </b-col>
        <b-col md="6">
          <b-card-body title="Horizontal Card">
            <b-card-text>
              This is a wider card with supporting text as a natural lead-in to additional content.
              This content is a little bit longer.
            </b-card-text>
          </b-card-body>
        </b-col>
      </b-row>
    </b-card>
  </div>
</template>
style

<style>
img {
  display: inline-block;
  width: 100%;
  height: 100%;
  background-size: contain;
  background-repeat: no-repeat;
  cursor: pointer;
  transition: all 200ms ease-in;
  filter: grayscale(1) opacity(.8);
}

img:hover {
  filter: grayscale(0) opacity(1);
}

</style>

これだけです

いかがでしたでしょうか?

白黒表示の画像をhoverするとカラーになるってなかなかクールですよね

下の記事も参照してより応用的なデザインにも挑戦してみてください

以上です


参考記事(応用編)
【Vueデザイン/コピペだけ】白黒画像一覧でhoverすると色がつくCSSアニメーション実装
スクリーンショット 2020-05-29 16.25.36.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js filter関数のはなし

今回のテーマ:子に配列を渡してデータを判別してもらう

Vue.js プロップスの話で親子コンポーネントのプロップスについて説明した。
今回は、プロップスのデータを配列にして、
カテゴリーを分けて、データを受け取りたい。

親コンポーネントを準備

今回は子にお使いをしてきてもらうので、買い物リストを作成します。:apple:
買い物リストには買うものの名前と、売り場を示すcategoryを追加しました。

Parent.vue
<template>
<Child :list = list></Child>
</template>
<script>
    import Child from "../components/Child";
    export default {
        components:{Child},
        data(){ 
            return{
                list:[
                    {name:"りんご",category:"果物"},
                    {name:"砂糖",category: "調味料"},
                    {name:"いちご",category:"果物"}
                ]
            }
        }
    }
</script>

子コンポーネントを準備

複数のものをリストアップされてるので。今回はArrayで受け取ります。
もらったリストを使って、お店の人に「〇〇ください」と伝えます。

Child.vue
<!--Parent.vueの子コンポーネント-->
<template>
    <div>
        {{lists[0].name}}ください

    </div>
</template>
<script>
export default {
    components:{},
    data:function(){
        return{}
    },
    props:{
        lists:{type:Array}
    }
}
</script>

v-for ="変数 in 配列"

v-forは、foreach文みたいなもの。v-bindでkeyを設定しなきゃいけない。
第二引数として配列のインデックスを受け取ることができる。
区切る文字inofでもいい。
v-forが指定した要素の中で、配列の中身をとりだして、それぞれに処理を行う。
繰り返したくないものは要素の外に出してあげる。

Child.vue
        スーパーにて、
        <div v-for="list in lists" v-bind:key="list.name">   //v-bindは省略できるので、:key=とも書くことができる。
            {{list.name}}ください
        </div>

スーパーなら上記のコードですべてが揃うけど、
今は果物屋にいるので、果物だけをピックアップして、「ください」と言いたい。

filter関数

配列の中身を特定の条件で絞り込みを行うメソッド。
何が書いてあるんだかよくわからない。

配列.filter
(絞り込む関数(変数)        //←この変数は配列を一つ一つ取り出した要素が入る
{絞り込み処理})           //上の行の「絞り込む関数」でこの行の「絞り込み処理」を呼び出している

今回、categoryが"果物"のものだけをピックアップしたい。

Child.vue
<script>
    computed:{
        fruits:function () {              //fruitsがfilter関数でピックアップしたものだけが入る変数
            return this.lists.filter(    //配列listsからfilter関数で値を返してください
                function (value) {       //valueを使ってfunctionという処理を行う。valueはlists)(配列)を一つ一つバラバラにしたものです
                    return value.category ==="果物"   //valueのcategoryが"果物"と完全一致したものだけを返します
                }
</script>

上記のコードで配列listsの中で、categoryが果物になっているものだけがfruitsに入りました。

ちなみに

computed(算出プロパティ)はmethodみたいなものだけど、methodは毎回処理される。
computedはデータが変わったら処理される。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vuetify] v-selectで[object, object] と表示された時の解決法

現象

items: [{id:1, text: 'テキスト'},{id:2, text: 'テキスト2'},{id:3, text: 'テキスト3'}]

上記のようなオブジェクトの配列をv-selectで表示。

<v-select
 :items="items"
/>

すると以下のように [object, object] と表示される。
スクリーンショット 2020-06-01 16.22.39.png

解決策

vuetifyの公式ページにオブジェクト使用時の記述があった。
スクリーンショット 2020-06-01 16.43.53.png

items-textを使う

item-text="オブジェクトのプロパティ名"使うことで解決

<v-select
 item-text="text"
 :items="items"
/>

スクリーンショット 2020-06-01 16.23.15.png

items-valueも使って値の指定を行う

あくまで表示はできたが、フィルタリングする時など、表示と裏で保持する値をそれぞれ分けたい場合は、 items-value を使う。

実際に値を確認するために、 @change="changedValue" を追記し、select選択時にconsole.logで値を出力する。

<v-select
 item-text="text"
 item-value="id"
 :items="items"
 @change="changedValue"
/>
methods: {
 changedValue(value) {
  console.log('value', value)
 }
}

表示
スクリーンショット 2020-06-01 16.23.15.png

スクリーンショット 2020-06-01 16.51.44.png

無事、表示と値を分けることができたので解決。

参考文献

https://vuetifyjs.com/ja/components/selects/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Web Components間のデータ通信について

背景

先日、下の投稿でVueコンポーネントのカスタム要素化を試して、無事に親(index.html)から呼び出し可能であることまでは確認しました。

https://qiita.com/yusuke-ka/items/7a46ff75e125f5659f4a

今回は、カスタム要素化したコンポーネントの親子間、子同士でデータのやり取りをしたい場合はどうするのか、について調べてみました。

準備

前回作った以下の3つのサンプルをそれぞれS3で静的ホスティング。

  • シンプルなVueのComponentをカスタム要素化したサンプル
  • Element-UIを使っているComponentをカスタム要素化したサンプル
  • Vuetifyを使っているComponentをカスタム要素化したサンプル

インデックスドキュメントに「bundle.js」を指定することで、それぞれ以下のような形でwebアクセスすればbundle.jsの中身が返ってくるようにしました。

http://simple-vue.s3-website-ap-northeast-1.amazonaws.com
http://simple-element-ui.s3-website-ap-northeast-1.amazonaws.com
http://simple-vuetify.s3-website-ap-northeast-1.amazonaws.com

これらを↓で調べたnuxt-serverless環境(といっても今回動作させるのはローカルのみ)から呼び出して表示してみます。

https://qiita.com/yusuke-ka/items/048eeaf936e74981f303

nuxt-serverlessのREADMEによると、/pagesにはエイリアスのみ記載したtsファイルを置いて、実際のvueファイルは/services/${serviceName}/pages/に置くみたい。

とりあえず、最初からあるtypescript.vueの中身を編集。

typescript.vue
<template>
  <div class="page-typescript">
    <h1>{{ greeting }}</h1>
    <vce-button></vce-button>
    <hr />
    <vce-table
      prop1="1"
      prop2="example text"
      prop3="true"
      string-prop="123"
      boolean-prop="false"
      number-prop="123"
      long-prop-name="long name"
    ></vce-table>
    <hr />
    <vce-tree></vce-tree>
  </div>
</template>

<script lang='ts'>
import { Component, Vue } from "nuxt-property-decorator";

@Component
export default class PageTypescript extends Vue {
  greeting: string = "Hello, TypeScript!";

  head() {
    return {
      title: "Hello, TypeScript",
      script: [
        {
          src: "http://simple-vue.s3-website-ap-northeast-1.amazonaws.com"
        },
        {
          src:
            "http://simple-element-ui.s3-website-ap-northeast-1.amazonaws.com"
        },
        {
          src: "http://simple-vuetify.s3-website-ap-northeast-1.amazonaws.com"
        }
      ]
    };
  }
}
</script>

画面はこんな感じ

> yarn dev

typescript.jpg

超シンプルなMicro Frontends環境が出来上がりました。

ちなみに、S3のbundle.jsを差し替えれば、親を再ビルドすることなくブラウザの再読み込みで子要素が更新されます。

要するに、それぞれのコンポーネントが独立してデプロイ(リリース)可能ということです。

データ通信

データ通信は以下の3つのデータのやり取りを確認。

  • 親から子にデータを送る
  • 子から親にデータを送る
  • 子から子にデータを送る

親から子にデータを送る

動的にデータ通信できることを確認したいので、親側のh1要素へのマウスオーバー/マウスリーブで、データが変わるようにし、その情報をボタンコンポーネントに送ってみる。

h1要素にmouseovermouseleavev-onを追加し、scriptタグ内にそれぞれイベント検知時のメソッドであるmouseover()mouseleave()を追加。

また、scriptタグ内にmessageを定義し、mouseover()mouseleave()内で値を変更する。

vce-buttonタグに「:message="message"」を追加し、messageの中身を子要素に送るようにして、親側の変更は完了。

typescript.vue
<template>
  <div class="page-typescript">
    <h1 v-on:mouseover="mouseover" v-on:mouseleave="mouseleave">{{ greeting }}</h1> 
    <vce-button :message="message"></vce-button>
    <hr />
    ...
  </div>
</template>

<script lang='ts'>
...

@Component
export default class PageTypescript extends Vue {
  ...
  message = "off";
  ...

  mouseover() {
    this.message = "on";
  }

  mouseleave() {
    this.message = "off";
  }
}
</script>

子要素側は以下。

propsmessageを受け取り、ボタン内に表示する。

Button.vue
<template>
  <div>
    <button>{{ text }} {{ message }}</button>
  </div>
</template>

<script>
export default {
  name: "button",
  props: {
    message: String,
  },
  data() {
    return {
      text: "button",
    };
  },
};
</script>

小さくて分かりづらいですが、ボタン内の文字列が微妙に変わっているのが分かるかと思います。

button.gif

子から親にデータを送る

今度は子コンポーネントから親コンポーネントにデータを送ってみる。

子コンポーネントは一カ所だけ修正。
buttonタグにクリックイベントを追加して、親のclickedイベントを呼び出し。

Button.vue
<template>
  <div>
    <button @click="$emit('clicked', 'クリック')">{{ text }} {{ message }}</button>
  </div>
</template>
...

親側はvce-button要素に子から呼び出されるclickedのイベントを追加して、その際の処理としてhandlerファンクションを追加。

handler内でmessageを更新しているので、これが子コンポーネントに伝わり、ボタンのラベルが変わるはず。

typescript.vue
<template>
  <div class="page-typescript">
    ...
    <vce-button :message="message" @clicked="handler"></vce-button>
    ...
</template>

<script lang='ts'>
  ...

  handler(event) {
    this.message = event.detail[0];
  }
}
</script>

子コンポーネントがclickedイベントを呼び出す際に引数として渡したデータの取り出し方がなんか通常と違っているような気がしますが(カスタム要素化したから??)、console.log(event)で確認したら、CustomEvent.detailに配列で入っていたので、上記のような取り出し方をしてみました。

正しいやり方なのかは分かりませんが、データを送るという目的は達成。

button2.gif

子から子にデータを送る

以下のサイトによると、3種類のやり方があるらしい。

https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87

  1. Use a global EventBus
  2. Use a simple global store
  3. Use the flux-like library Vuex

1はあまり推奨されないとのことなので、とりあえず3を試したが、結局上手くいかず。

それぞれのコンポーネントでStoreを使うことはできるが、それぞれ別々のStoreになってしまい、データを共有することができないという状況。

カスタム要素化していることが原因か、他所でデプロイしたものを読み込んでいるのが原因かわからないが、インスタンスがうまく共有できていないことが原因なのであれば、1や2の方法でも厳しいかもしれない。

もう少し時間をかけて調べれば、やり方が見つかるのかもしれないけど、疲れたので、正攻法でのデータ送信は一旦諦めることに。。。

子から子にデータを送る (代替案)

これまでの確認で親を経由すれば実現できるのは明らかなので、親を活用したやり方を考えてみる。

ただ、親コンポーネントが子コンポーネントの実装にいちいち関与してたら、独立してデプロイできるようにした意味がないので、対象データの送信元と利用先だけがそれに関わる実装をすれば良いという状況を作りたい。

イメージはこんな感じ。

  1. 親コンポーネントでデータを一元管理(枠だけ用意するイメージ)。
  2. データはkey/valueのオブジェクトで、親コンポーネントは中のkeyやvalueに関与しない。
  3. 親コンポーネントにデータ更新用のメソッドを作成し、引数でkeyとvalueを受け取ってデータを更新する。
  4. 子コンポーネントがkeyとvalueを指定して、カスタムイベント経由で親に定義した更新用のメソッドを呼び出す。
  5. データが更新されるとVueのリアクティブデータの機能で、すべての子コンポーネントに更新後のデータを送信する。
  6. 子コンポーネントは親コンポーネントから、オブジェクトのデータを受け取り、自分が必要な要素のkeyを指定して値を取り出す。

とりあえず思いついた案を試してみる。

まず親コンポーネントから。

typescript.vue
<template>
  <div class="page-typescript">
    ...
    <vce-button
      ...
      :param="getParam"
      @commit="commit($event)"
    ></vce-button>
    ...
    <vce-table
      ...
      :param="getParam"
      @commit="commit($event)"
    ></vce-table>
    ...
    <vce-tree :param="getParam" @commit="commit($event)"></vce-tree>
  </div>
</template>

template内のすべての子コンポーネントのタグに、何も考えずに以下の2つを追加する。
「:param="getParam()"」
@commit="commit($event)"」

上がデータ配布用。
下がデータ更新用。

※今回、データはparamという名前で扱うことにした。

次がscript部分。

typescript.vue
<script lang="ts">
...
@Component
export default class PageTypescript extends Vue {
  ...
  param: any = {};

  ...

  get getParam() {
    return JSON.stringify(this.param);
  }

  commit(event) {
    this.$set(this.param, event.detail[0], event.detail[1]);
  }
}
</script>

「param: any = {};」でparamという名前でデータを定義。

「get getParam()」はデータを文字列化して返している。
最初はオブジェクトのまま渡そうかと思っていたが、カスタム要素化しているためか、子コンポーネントに渡った時に[object Object]という文字列になってしまうので、仕方なくこうした。
なお、算出プロパティにしないと、無駄に子コンポーネントの数だけ実行されてしまう。

「commit(event)」は子コンポーネント側が渡してきたkeyとvalueをparamに登録している。
vueはオブジェクトの要素追加や削除をリアクティブに検知できないので、単純にthis.paramに要素を追加するのではなく、this.$set()を使って要素を追加している。

続いて、データを更新する子コンポーネント。

以前、カスタム要素化したツリーのコンポーネントを使って、ツリーの要素が選択されたら、親のイベントを呼んでデータを更新するようにしてみる。

tree.vue
<template>
  <div id="app">
    <v-app id="inspire">
      <v-treeview
        ...
        @input="handler"
      ></v-treeview>
    </v-app>
  </div>
</template>

vuetifyのツリー要素選択のイベントがinputということだったので、「@input="handler"」でhandler()メソッドを呼ぶようにしている。

続いてscript

tree.vue
<script>
...
export default {
  ...
  methods: {
    handler(selected) {
      this.$emit("commit", "key", selected);
    },
  },
  props: {
    param: String,
  },
  ...
};
</script>

methodshandlerメソッドを追加。引数のselectedには、選択しているツリー要素のIDが配列で入っている(vuetifyが入れてくれる)。
$emit()で親コンポーネントのcommitイベントを呼び出している。

propsparamを追加しているが、今回は使っていないのであまり意味はない。

続いて、データを受け取る側の子コンポーネント。
またボタンコンポーネントを利用。

button.vue
<template>
  <div>
    <button @click="$emit('clicked', 'クリック')">
      {{ text }} {{ message }} {{ getParam }}
    </button>
  </div>
</template>

{{ getParam }}を追加しただけ。

script部分。

button.vue
<script>
export default {
  name: "button",
  props: {
    message: String,
    param: String,
  },
  data() {
    return {
      text: "button",
      param: "",
    };
  },
  computed: {
    getParam() {
      var map = JSON.parse(this.param);
      return map["key"];
    },
  },
};
</script>

propsparam: Stringを追加して親から受け取れるようにしている。
ちなみに、String部分をObjectにすればオブジェクトのまま受け取れるかと思ったが、駄目だった。

dataにもparamを追加。propsにあるからいらねんじゃね?と思ったが、他でthis.paramを呼び出したときに怒られたので、追加した。これは、もっと他に上手いやり方がありそう。

最後、computedにgetParam()を追加。this.paramは文字列として渡ってくるので、オブジェクト化した後に、指定したキーに対応する値を取得して返している。

同様にして、別の子コンポーネントでも同じデータを受け取れるようにした。
(一つ目の子コンポーネントにしかデータが送れないということがありそうな気がしたので。)

table.vue
<template>
  <div class="card card--primary">
    <h4>{{ message }} {{ getParam }}</h4>
    ...
  </div>
</template>

<script>
export default {
  props: {
    param: String,
    ...
  },
  data() {
    return {
      message: "Custom Element By Vue + Element UI",
      param: "",
    };
  },
  computed: {
    getParam() {
      var map = JSON.parse(this.param);
      return map["key"];
    },
    ...
  },
  ...
};
</script>

説明は省略。

動作確認してみる。

button3.gif

見づらいですが、一応、ツリーで選択した要素のIDが、ボタンのコンポーネント(ボタンのラベル)とテーブルのコンポーネント(タイトル部分の横)に動的に表示されています。

さいごに

今回はWeb Components間のデータ通信について調べてみました。

Custom Element化(さらに呼び出し部分を疎結合に)したことで、通常のVueのコンポーネントと異なる動作をする部分があり、なかなか上手くいきませんでした。

特に子コンポーネント間のデータ通信は、Vuexなどによっていい感じにデータを管理できないか、もうちょっと調べてみたいところです。

最後の「子から子にデータを送る (代替案)」については、Vuexのようにどのコンポーネントからもアクセスできるようにはなっていませんが、親コンポーネント側は最初に今回の実装を入れてしまえば、後はノータッチでいけるので、負担は幾分か減ると思います。
ただ、利用のルールは決めておかないと、キーが重複したりとか問題が発生しそうですね。

まあ、わざわざコンポーネントを疎結合にしているわけだから、各コンポーネントで必要な"状態"は各コンポーネント内で持ってもらって、他とのデータのやり取りは最低限にすれば、そんなに大変なことにならない気もしますが…。

あと、親コンポーネントが普通のVueコンポーネントも一緒に抱えるなら、データを一元管理する部分はVuexとか使ってもいいかもです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express.jsにPassport.jsで、任意のフォルダ配下のみをBasic認証する

概要

ExpressフレームワークにBasic認証を適用する方法について記載します。

任意のフォルダのみにBasic認証を適用するものとし、そのフォルダにVue-CLIによるSPA(=Single Page Application)を置くことを目的とします。

方針

最小限のBasic認証を認証する場合はExpress.jsプロジェクトにて提供されるbasic-auth-connectライブラリでもよいのですが、他の認証への差し替えの容易性を考慮して、Passport.js を用いることとします。

認証を要求する領域と、不要とする領域を共存させたいので、任意のフォルダ配下のみをBasic認証の対象とします。

ついでに、Vue CLIで作成したSPAを任意フォルダへ配置するものとします。

なお、ユーザー名は「user」でパスワードは「pass」とします。本サンプルコードは動作確認が目的なので、パスワード管理はガバガバです。

(補足)Express3とExpress4の違い(basic-auth-coonect)

Express.jsでBasic認証を、で検索すると「express.basicAuth()」での実装例が時々ひっかかります。これは、Express3での記法とのことです。現在のExpress4では、「var basicAuth = require('basic-auth-connect');」とする仕組みに変わっています。

ref. https://qiita.com/zaru/items/51b415c80245920837ff

前提条件

AzureのWebアプリ(PaaS)を利用して公開する前提で、Express.jsを構築します。
具体的には、次のページの手順に従って作成します。

作成し終えたファイル一式は以下のようになります(※Mocha.jsでのテストを前提としてtestフォルダも作成しているが、この記事では使わない)。

ref. https://github.com/hoshimado/tdd-vuejs-book/tree/master/appendix/b-azure-express-mocha

先ずは、全体をBasic認証の対象とする方法

Passport.jsの必要なモジュールを次のようにインストールします。

npm install passport passport-http --save 

続いて、app.jsを次のように変更します。

// +++ ここから +++
var passport = require('passport');
var passportHttp = require('passport-http');
passport.use(new passportHttp.BasicStrategy(
    function (username, password, done) {
        if(username=='user' && password=='pass'){
            // ユーザー名とパスワードが有効なら true を返却する。
            return done(null,true);
        }else{
            // 無効なら、false を返却する(とUnauthorizedを画面表示)
            return done(null, false);
        }
    }
));
app.use('/', passport.authenticate('basic',{session: false}) );
// --- ここまで ---
app.use(express.static(path.join(__dirname, 'public'))); // この行の前に、↑を追加する。

この状態で、サーバーを「npm run dev」で起動すると、全体に対してBasic認証が設定されます。

次に、任意のフォルダ配下のみをBasic認証の対象とする方法

先の節で追加した部分を元に戻します。

本サンプルでは「/auth/byvue」へのアクセスに対してのみ、Basic認証を掛けるものとします。

任意のフォルダ配下のみにBasic認証を掛けるには「ルーティング設定で対象フォルダに対してpassport.authenticate()のミドルウェアを指定し、認証成功時にexpress.static()で静的ファイルを表示する(か、次のルーティングへ渡す)」ようにします。

具体的には、例えば次のようにします。(※本サンプルでは、対象を静的ファイルとします)。

  1. app.jsで、publicフォルダ配下の静的ファイルを処理している1行をコメントアウト
  2. 制御用のファイルroutes/auth.jsを作成して、対象パスへのアクセスをそちらへルーティング
app.js
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// app.use(express.static(path.join(__dirname, 'public'))); // ★コメントアウトする


//app.use('/', indexRouter); // ★利用していないので、ここもコメントアウトする
app.use('/auth', require('./routes/auth')); // ★追加する
app.use('/users', usersRouter);

module.exports = app;
  1. 作成したファイルroutes/auth.jsに次を記述
  2. auth/byvueフォルダと、auth/simplyフォルダ配下に適当な表示用htmlファイルを置く
auth.js
var express = require('express');
var path = require('path');
var router = express.Router();


var passport = require('passport');
var passportHttp = require('passport-http');
passport.use(new passportHttp.BasicStrategy(
    function (username, password, done) {
        if(username=='user' && password=='pass'){
            return done(null,true);
        }else{
            return done(null, false);
        }
    }
));


router.use('/byvue', passport.authenticate('basic',{session: false}), express.static(path.join(__dirname, '../auth/byvue')) ); // ★ここにだけBasic認証を掛ける
router.use('/', express.static(path.join(__dirname, '../auth')) ); // ★それ以外はそのまま表示する。

module.exports = router;

上記までを終えたら、「npm run dev」でhttpサーバーを起動します。

下記へアクセスすると、Basic認証を求められます。
(※本サンプルでは、express.static()でマッピングするフォルダを実際のパスに合わせてますが、異なるフォルダをマッピングしてももちろん構いません。)

http://localhost:3000/auth/byvue/

それ以外の、例えば下記へアクセスすると、Basic認証無しで表示されます。

http://localhost:3000/auth/simply/

もちろん、下記へのアクセスもBasic認証無し、となります。

http://localhost:3000/auth/

http://localhost:3000/

Basic認証については以上です。

オマケとして、任意フォルダ配下へVue CLIでBuildしたファイルを配置する方法

オマケです(当方にとっては、こっちが目的だったので)。

上記の「auth/byvue」のフォルダに、Vue CLIで作成したファイルを配置するには、次のようにします。本サンプルではExpress用のsrcフォルダと同じ階層にVue CLIのプロジェクトをcli-vueという名称で作成したと仮定します。

  1. cli-vueフォルダ直下に、vue.config.jsファイルを次のように配置する(ファイル名は固定)
  2. 「npm run build」でビルドする
vue.config.js
module.exports = {
    // options...
    outputDir : '../src/auth/byvue',
    publicPath : './'
}

ここで、それぞれのオプションは以下を意味します。

  • outputDir : ビルドしたファイルの出力先フォルダパス。このフォルダは毎回「削除→再作成」されることに注意。
  • publicPath : ビルドしたhtmlファイルの「基底URL」(HTMLのbaseタグ)。デフォルトでは絶対パスの「/」。
    • 今回のサンプルでは「サブフォルダ」に配置したかったので、「相対パス(./)」で扱うように設定

ref. https://cli.vuejs.org/config/#publicpath

上記までを終えたら、「npm run dev」でhttpサーバーを起動します。

下記へアクセスすると、Basic認証を経たのちにVue CLIで作成されたページが表示されます。

http://localhost:3000/auth/byvue/

以上ー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.JSで論理演算子を使う

v-ifや-showで複数の条件を指定したい場合。
Javascriptで用意されている物を使うことができる。
論理演算子 - JavaScript | MDN

AかつB

v-if = "A && B"

AまたはB

v-if = "A || B"

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。

FIRST PLAN株式会社のフロントエンドエンジニアtakeです。

sampleText

さて、今回はテストでログインフォームを作ったので良ければご覧ください。

こんな事をやりました

Image from Gyazo

環境設定

まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。

$ composer require laravel/ui
$ php artisan ui vue --auth

すぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。

そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。

$ npm install bootstrap-vue bootstrap

でインストールし、
以下のコードをメインのjsファイルに追加します。

import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);

後は好みの問題なのですが、FontAwesomevue-sweetalert2をインストールします。

vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。

FontAwesome

$ npm install --save @fortawesome/fontawesome-free
import '@fortawesome/fontawesome-free/css/all.css';

vue-sweetalert2

$ import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
Vue.use(VueSweetalert2);

コード

html

今回は以下のような感じで、cardinput-groupを使って実装しました。

HTML
<template>
  <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center">
    <div class="login-form__field card rounded-0 pt-5 pb-3">
      <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i>
      <div class="card-body">
        <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1>

        <hr class="bg-primary mt-0 mb-4">

        <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-user"></i>
            </span>
          </div>
          <input class="form-control" type="text" placeholder="Username" v-model="login.user_name">
        </div>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-key"></i>
            </span>
          </div>
          <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password">
        </div>

        <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button>

        <div class="login-form__attention d-flex justify-content-between my-o mx-auto">
          <div class="custom-control custom-checkbox">
            <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember">
            <label class="custom-control-label" for="remember-me">情報を記憶する</label>
          </div>
          <a class="card-link" href="#">
            <u>パスワードを忘れた方はこちら</u>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>

Scss

今回はBootstrapベースなので、BEMを採用して影のエフェクト以外は軽く整える程度にしました。

scss
.login-form {
  &__field {
    position: relative;
    max-width: 30rem;
    //影エフェクト
    &:after {
      content: '';
      position: absolute;
      bottom: 20px;
      right: 55px;
      z-index: -1;
      width: 50%;
      height: 100px;
      box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3);
      transform: skew(-40deg);
    }
  } //__field
  &__budge {
    position: absolute;
    top: -2rem;
    left: 50%;
    font-size: 6rem;
    transform: translateX(-50%);
  } //__budge
  &__attention {
    font-size: 0.6rem;
  } //__attention
  &__button {
    border-radius: 0!important;
  } //__button
} // .login-form

JavaScript

これがデフォルトのVueの記述方法になります。
ざっくり説明するとdataは変数等を格納する場所・computedは変更があるとリアルタイムで値を再計算してくれる場所・methodsは文字通りメソッドを格納する場所です。
他にもwatchやライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。

まずdata内のloginは「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"といった記述があると思いますが、このv-modelによって双方向データバインディングを実現しています。
これによりデータと表示の同期・DOM操作を自力でやる必要がなくなります。

次はcomputedですが、今回はlogin.user_namelogin.passwordが条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)と指定されている関数がありますが、このように使用する事で、他の場所で毎回入力を確認する必要がなくなります。

最後にmethodsは一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-modelで同期した値をsweetalert2で表示しています。

JavaScript
export default {
  data() {
    return {
      //ログイン情報
      login: {
        user_name: '',
        password: '',
        remember: false
      }
    }
  },
  computed: {
    //ユザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる
    checkInput() {
      return (!this.login.user_name || !this.login.password) ? true : false;
    }
  },
  methods: {
    signIn() {
      //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。
      if (!this.checkInput) {
        //v-modelで同期した値を出力する
        this.$swal({
          icon: 'info',
          title: 'Information',
          text: `
            ID: ${this.login.user_name},
            Password: ${this.login.password},
            Remember: ${this.login.remember}
          `
        })
      }
      return;
    } //signIn
  }
}

最後に

$ npm run dev

でコンパイルして確認すると...

Image from Gyazo

キチンと動いています。

よろしければ参考にしてみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。

FIRST PLAN株式会社のフロントエンドエンジニアtakeです。

sampleText

さて、今回はテストでログインフォームを作ったので良ければご覧ください。

こんな事をやりました

Image from Gyazo

環境設定

まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。

$ composer require laravel/ui
$ php artisan ui vue --auth

すぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。

そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。

$ npm install bootstrap-vue bootstrap

でインストールし、
以下のコードをメインのjsファイルに追加します。

import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);

後は好みの問題なのですが、FontAwesomevue-sweetalert2をインストールします。

vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。

FontAwesome

$ npm install --save @fortawesome/fontawesome-free
import '@fortawesome/fontawesome-free/css/all.css';

vue-sweetalert2

$ import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
Vue.use(VueSweetalert2);

コード

HTML

今回は以下のような感じで、cardinput-groupを使って実装しました。

HTML
<template>
  <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center">
    <div class="login-form__field card rounded-0 pt-5 pb-3">
      <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i>
      <div class="card-body">
        <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1>

        <hr class="bg-primary mt-0 mb-4">

        <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-user"></i>
            </span>
          </div>
          <input class="form-control" type="text" placeholder="Username" v-model="login.user_name">
        </div>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-key"></i>
            </span>
          </div>
          <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password">
        </div>

        <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button>

        <div class="login-form__attention d-flex justify-content-between my-o mx-auto">
          <div class="custom-control custom-checkbox">
            <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember">
            <label class="custom-control-label" for="remember-me">情報を記憶する</label>
          </div>
          <a class="card-link" href="#">
            <u>パスワードを忘れた方はこちら</u>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>

CSS

飽くまでBootstrapベースなので、scssでBEMを採用して影のエフェクト以外は軽く整える程度にしました。

scss
.login-form {
  &__field {
    position: relative;
    max-width: 30rem;
    //影エフェクト
    &:after {
      content: '';
      position: absolute;
      bottom: 20px;
      right: 55px;
      z-index: -1;
      width: 50%;
      height: 100px;
      box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3);
      transform: skew(-40deg);
    }
  } //__field
  &__budge {
    position: absolute;
    top: -2rem;
    left: 50%;
    font-size: 6rem;
    transform: translateX(-50%);
  } //__budge
  &__attention {
    font-size: 0.6rem;
  } //__attention
  &__button {
    border-radius: 0!important;
  } //__button
} // .login-form

JavaScript

以下がデフォルトのVueの記述方法になります。

ざっくり説明するとdataは変数等を格納する場所・computedは変更があるとリアルタイムで値を再計算してくれる場所・methodsは文字通りメソッドを格納する場所です。
他にもwatchやライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。

さて、まずdata内のloginは「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"といった記述があると思いますが、このv-modelによって双方向データバインディングを実現しています。
どちらかが変更された段階でもう片方にも自動で変更が同期されるため、自分で逐一document.querySelector(element)~をする必要がなくなります。

次はcomputedですが、今回はlogin.user_namelogin.passwordが条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)と指定されている関数がありますが、このように使用する事で他の場所で毎回入力を確認する必要がなくなります。
ちなみにbuttonにある:disabled="checkInput"にも使用されていますが、まずこの:マークはv-bindといってhtml内の属性(classhref等)に付ける事で内部でJavaScriptのコードを直接使用可能にするものです。
今回はこれらを利用してdisabledの値をフォーム入力の有無によって動的に変更しています。

最後にmethodsは一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-modelで同期した値をsweetalert2で表示しています。
イベントハンドリングはhtml側で@event="hoge"のように行います。(今回の場合は@click="signIn"
ちなみにこの@click以下はコンパイル時に削除されるため、htmlを汚染する心配はありません。

JavaScript
export default {
  data() {
    return {
      //ログイン情報
      login: {
        user_name: '',
        password: '',
        remember: false
      }
    }
  },
  computed: {
    //ユーザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる
    checkInput() {
      return (!this.login.user_name || !this.login.password) ? true : false;
    }
  },
  methods: {
    signIn() {
      //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。
      if (!this.checkInput) {
        //v-modelで同期した値を出力する
        this.$swal({
          icon: 'info',
          title: 'Information',
          text: `
            ID: ${this.login.user_name},
            Password: ${this.login.password},
            Remember: ${this.login.remember}
          `
        })
      }
      return;
    } //signIn
  }
}

最後に

$ npm run dev

でコンパイルして確認すると...

Image from Gyazo

キチンと動いています。

よろしければ参考にしてみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetifyをインストールした環境でJestを実行する設定

Vuetifyをインストールした環境で、Jestを利用するときにしておいた方がいい設定です。

それぞれの設定は、tests/unitディレクトリに作成したsetup.jsを書きます。tests/unit/setup.jsの設定を反映するには、jest.config.jsに、以下を設定します。

jest.config.js
"setupFiles": ["./tests/unit/setup.js"]

警告を出力しない設定

[Vue warn]: Unknown custom element:〜

VuetifyのコンポーネントをJestが認識しないために出力される警告です。tests/unit/setup.jsに以下を設定することで出力されなくなります。

tests/unit/setup.js
import Vue from "vue";
import Vuetify from "vuetify";

Vue.use(Vuetify);

Download the Vue Devtools extension for a better development experience:〜

ブラウザの場合にVue Devtoolsの拡張機能をインストールすることを勧めるメッセージのようですが、Jestの環境には意味のないメッセージです。tests/unit/setup.jsに以下の設定をすることで出力されなくなります。

tests/unit/setup.js
Vue.config.devtools = false;

You are running Vue in development mode.〜

production環境へデプロイするときにproduction modeに切り替えることを注意するメッセージのようですが、これもテストの実行中には意味のないメッセージです。

tests/unit/setup.jsに以下の設定をすることで出力されなくなります。

tests/unit/setup.js
Vue.config.productionTip = false;

ボタンの置き替え

Vuetifyをインストールした環境では、buttonの見た目が変わってしまいます。下の図のように、ボタンの枠がなくなって、立体的に見えなくなっています。

vuetify-button.png

以下のようにv-btnに置き換えることができます。

<v-btn id="vBtnClick" @click="onVbtnClick">v-btn</v-btn>

クリックされた場合に、onVbtnClickメソッドを呼び出すように設定しています。

ブラウザで実行した場合には、onVbtnClickメソッドが呼び出されます。しかし、以下のテストではonVbtnClickメソッドが呼び出されないで失敗します。

describe("v-btnがクリックしたときに、割り当てられたメソッドが呼び出されたことを確認する", () => {
  it("v-btnがクリックしたときに、割り当てられたメソッドが呼び出されたことを確認する", () => {
    const wrapper = shallowMount(SampleTextArea);
    const onVbtnClick = jest.fn();
    wrapper.setMethods({onVbtnClick});
    wrapper.find("#vBtnClick").trigger("click");
    expect(onVbtnClick).toHaveBeenCalledTimes(1);
  });
});

v-btnの設定を以下のように変えることでテストは成功します。

<v-btn id="vBtnClick" @click.native="onVbtnClick">v-btn</v-btn>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ノーバンドルなビルドツール「Vite」を試してみる【no bundle dev enviroment for Vue 3.0】

最近猛烈にスターを集めているビルドツール、Viteを触ってみたので簡単に紹介します。
この記事はVite v0.19.1時点での情報です。

Viteとは?

ViteはVue.jsの作者のEvan You氏が開発中のノーバンドルなビルドツールです。
ネイティブのESモジュールのインポートを利用しバンドル不要で高速に動作するdevサーバーと、Rollup.jsをベースとしたプロダクションビルド機能を提供します。
設定不要で.vueのSFCをコンパイルできて、さらにデフォルトで今開発中のVue3.0が使えます。
しかも、vue-cliのようにVue.js限定ではなく、React、Preactにも対応しています。

スクリーンショット 2020-06-01 5.59.11.png

:warning: 注意

Still experimental, but we intend to make it suitable for production.

とある通り、まだ絶賛開発中です。プロダクションで使うのは控えた方が良さそうです。

何が嬉しいの?

VueのSFC(Single File Components)での開発ならVue CLIで良いのでは?と思うかもしれません。
Viteを使う利点は以下の通りです。

  • 開発時はバンドル不要で動作するので、大規模プロジェクトでも初回起動が非常に早い
  • HMR(画面の再描画無しにファイル変更をブラウザに適用してくれる機能)が、モジュールの総数と切り離されているため一貫して高速に動作する

ざっくり言うと総じて動作が早いです。開発体験が良い。
他詳細な説明はREADMEにも記載されているのでそちらもどうぞ。

https://github.com/vitejs/vite#how-and-why

インストールとプロジェクト作成

VueとReactのプロジェクト作成を簡単に説明します。

Vue

以下コマンドでプロジェクトを作成します。

$ npm init vite-app example-vue

これだけでOKです。example-vueにVueのプロジェクトが作成されます。
あとは依存モジュールをinstallして起動するだけです。

$ cd example-vue
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.42.05.png

React

--template reactでReactのテンプレートを指定したうえでプロジェクトを作成します。

$ npm init vite-app --template react example-react

example-reactにReactのプロジェクトが作成されます。
あとはvueと同じく起動するだけです。

$ cd example-react
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.47.13.png

ビルド

ViteではRollup.jsを内部的に使ってリソースをバンドルしプロダクションビルドが可能です。
buildコマンドでビルドが実行されます。

$ npm run build

dist配下に成果物が生成されるので、あとはそれを公開するだけです。

細かいビルドオプションを設定したい場合は、コマンド引数で指定するか、設定ファイルを作り記述できます。
以下成果物のディレクトリをoutディレクトリに変更する例です。

プロジェクトルートにvite.config.jsを追加して以下を記述します。

vite.config.js
module.exports = {
  outDir: "out"
}

あとはそのままビルドすればoutディレクトリに成果物が生成されます。

他設定可能なオプションはソースコードをみるのが良さそうです。
https://github.com/vitejs/vite/blob/master/src/node/config.ts

TypeScriptへの対応

TypeScriptへの対応も設定なしで可能です。Vueの場合は.vue<script lang="ts">を設定するだけ。
Reactの場合は、.jsx.tsxに変更するだけです。
.tsファイルもそのままimportできます。

App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue + Vite" />
</template>

<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { defineComponent } from 'vue'

// Composition APIなので defineComponent()を利用
export default defineComponent({
  name: 'App',
  components: {
    HelloWorld
  },
  setup() {
    return {}
  }
})
</script>

:warning: 注意点
2020/05/31現在、TypeScriptはJSへのトランスパイルのみ対応していて、型チェックは行えません。
基本的にエディタ上での型エラーの確認と、tsconfig.jsonを追加しビルド前にtsc --noEmitを利用して型チェックを行ってください。

PostCSS、Scssへの対応

Viteは、.vueファイルとインポートされた.cssファイルの全てにPostCSSを自動的に適用します。
必要なPostCSSのプラグインをnpmでインストールして、あとはpostcss.config.jsをプロジェクトルートに追加するだけです。

Autoprefixerの利用例。

$ npm i autoprefixer --save-dev
postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')()
  ]
}

CSS Pre-Processorsのscssも、sassをnpmインストールして、.vueファイルで<style lang="scss">を指定するだけで利用できます。

$ npm i sass --save-dev
<style lang="scss">
/* scss */
</style>

終わりに

とても高速に動作するのでViteかなり良いですね。あと、Vue 3.0のsandbox環境としても最高です。
今回紹介した以外にも機能盛り沢山なので、是非READMEを読んで欲しいです。
https://github.com/vitejs/vite

今後もさらに機能が拡張されるようなので引き続き動向を見ていきたいです。

最後に。Evan You氏のVite作成時の投稿がカッコ良すぎる。
こんなこといつか言ってみたいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ノーバンドルな開発ツール「Vite」を試してみる【no bundle dev enviroment for Vue 3.0】

最近猛烈にスターを集めている開発ツール、Viteを触ってみたので簡単に紹介します。
この記事はVite v0.19.1時点での情報です。

Viteとは?

ViteはVue.jsの作者のEvan You氏が開発中のノーバンドルな開発ツールです。
ネイティブのESモジュールのインポートを利用しバンドル不要で高速に動作するdevサーバーと、Rollup.jsをベースとしたプロダクションビルド機能を提供します。
設定不要で.vueのSFCをコンパイルできて、さらにデフォルトで今開発中のVue3.0が使えます。
しかも、vue-cliのようにVue.js限定ではなく、React、Preactにも対応しています。

スクリーンショット 2020-06-01 5.59.11.png

:warning: 注意

Still experimental, but we intend to make it suitable for production.

とある通り、まだ絶賛開発中です。プロダクションで使うのは控えた方が良さそうです。

何が嬉しいの?

VueのSFC(Single File Components)での開発ならVue CLIで良いのでは?と思うかもしれません。
Viteを使う利点は以下の通りです。

  • 開発時はバンドル不要で動作するので、大規模プロジェクトでも初回起動が非常に早い
  • HMR(画面の再描画無しにファイル変更をブラウザに適用してくれる機能)が、モジュールの総数と切り離されているため一貫して高速に動作する

ざっくり言うと総じて動作が早いです。開発体験が良い。
他詳細な説明はREADMEにも記載されているのでそちらもどうぞ。

https://github.com/vitejs/vite#how-and-why

インストールとプロジェクト作成

VueとReactのプロジェクト作成を簡単に説明します。

Vue

以下コマンドでプロジェクトを作成します。

$ npm init vite-app example-vue

これだけでOKです。example-vueにVueのプロジェクトが作成されます。
あとは依存モジュールをinstallして起動するだけです。

$ cd example-vue
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.42.05.png

React

--template reactでReactのテンプレートを指定したうえでプロジェクトを作成します。

$ npm init vite-app --template react example-react

example-reactにReactのプロジェクトが作成されます。
あとはvueと同じく起動するだけです。

$ cd example-react
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.47.13.png

ビルド

ViteではRollup.jsを内部的に使ってリソースをバンドルしプロダクションビルドが可能です。
buildコマンドでビルドが実行されます。

$ npm run build

dist配下に成果物が生成されるので、あとはそれを公開するだけです。

細かいビルドオプションを設定したい場合は、コマンド引数で指定するか、設定ファイルを作り記述できます。
以下成果物のディレクトリをoutディレクトリに変更する例です。

プロジェクトルートにvite.config.jsを追加して以下を記述します。

vite.config.js
module.exports = {
  outDir: "out"
}

あとはそのままビルドすればoutディレクトリに成果物が生成されます。

他設定可能なオプションはソースコードをみるのが良さそうです。
https://github.com/vitejs/vite/blob/master/src/node/config.ts

TypeScriptへの対応

TypeScriptへの対応も設定なしで可能です。Vueの場合は.vue<script lang="ts">を設定するだけ。
Reactの場合は、.jsx.tsxに変更するだけです。
.tsファイルもそのままimportできます。

App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue + Vite" />
</template>

<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { defineComponent } from 'vue'

// Composition APIなので defineComponent()を利用
export default defineComponent({
  name: 'App',
  components: {
    HelloWorld
  },
  setup() {
    return {}
  }
})
</script>

:warning: 注意点
2020/05/31現在、TypeScriptはJSへのトランスパイルのみ対応していて、型チェックは行えません。
基本的にエディタ上での型エラーの確認と、tsconfig.jsonを追加しビルド前にtsc --noEmitを利用して型チェックを行ってください。

PostCSS、Scssへの対応

Viteは、.vueファイルとインポートされた.cssファイルの全てにPostCSSを自動的に適用します。
必要なPostCSSのプラグインをnpmでインストールして、あとはpostcss.config.jsをプロジェクトルートに追加するだけです。

Autoprefixerの利用例。

$ npm i autoprefixer --save-dev
postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')()
  ]
}

CSS Pre-Processorsのscssも、sassをnpmインストールして、.vueファイルで<style lang="scss">を指定するだけで利用できます。

$ npm i sass --save-dev
<style lang="scss">
/* scss */
</style>

終わりに

とても高速に動作するのでViteかなり良いですね。あと、Vue 3.0のsandbox環境としても最高です。
今回紹介した以外にも機能盛り沢山なので、是非READMEを読んで欲しいです。
https://github.com/vitejs/vite

今後もさらに機能が拡張されるようなので引き続き動向を見ていきたいです。

最後に。Evan You氏のVite作成時の投稿がカッコ良すぎる。
こんなこといつか言ってみたいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ノーバンドルな開発ツール「Vite」を触ってみる 【no bundle dev enviroment for Vue 3.0】

最近猛烈にスターを集めている開発ツール、Viteを触ってみたので簡単に紹介します。
この記事はVite v0.19.1時点での情報です。

Viteとは?

ViteはVue.jsの作者のEvan You氏が開発中のノーバンドルな開発ツールです。
ネイティブのESモジュールのインポートを利用しバンドル不要で高速に動作するdevサーバーと、Rollup.jsをベースとしたプロダクションビルド機能を提供します。
設定不要で.vueのSFCをコンパイルできて、さらにデフォルトで今開発中のVue3.0が使えます。
しかも、vue-cliのようにVue.js限定ではなく、React、Preactにも対応しています。

スクリーンショット 2020-06-01 5.59.11.png

:warning: 注意

Still experimental, but we intend to make it suitable for production.

とある通り、まだ絶賛開発中です。プロダクションで使うのは控えた方が良さそうです。

何が嬉しいの?

VueのSFC(Single File Components)での開発ならVue CLIで良いのでは?と思うかもしれません。
Viteを使う利点は以下の通りです。

  • 開発時はバンドル不要で動作するので、大規模プロジェクトでも初回起動が非常に早い
  • HMR(画面の再描画無しにファイル変更をブラウザに適用してくれる機能)が、モジュールの総数と切り離されているため一貫して高速に動作する

ざっくり言うと総じて動作が早いです。開発体験が良い。
他詳細な説明はREADMEにも記載されているのでそちらもどうぞ。

https://github.com/vitejs/vite#how-and-why

インストールとプロジェクト作成

VueとReactのプロジェクト作成を簡単に説明します。

Vue

以下コマンドでプロジェクトを作成します。

$ npm init vite-app example-vue

これだけでOKです。example-vueにVueのプロジェクトが作成されます。
あとは依存モジュールをinstallして起動するだけです。

$ cd example-vue
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.42.05.png

React

--template reactでReactのテンプレートを指定したうえでプロジェクトを作成します。

$ npm init vite-app --template react example-react

example-reactにReactのプロジェクトが作成されます。
あとはvueと同じく起動するだけです。

$ cd example-react
$ npm i
$ npm run dev

スクリーンショット 2020-05-31 20.47.13.png

ビルド

ViteではRollup.jsを内部的に使ってリソースをバンドルしプロダクションビルドが可能です。
buildコマンドでビルドが実行されます。

$ npm run build

dist配下に成果物が生成されるので、あとはそれを公開するだけです。

細かいビルドオプションを設定したい場合は、コマンド引数で指定するか、設定ファイルを作り記述できます。
以下成果物のディレクトリをoutディレクトリに変更する例です。

プロジェクトルートにvite.config.jsを追加して以下を記述します。

vite.config.js
module.exports = {
  outDir: "out"
}

あとはそのままビルドすればoutディレクトリに成果物が生成されます。

他設定可能なオプションはソースコードをみるのが良さそうです。
https://github.com/vitejs/vite/blob/master/src/node/config.ts

TypeScriptへの対応

TypeScriptへの対応も設定なしで可能です。Vueの場合は.vue<script lang="ts">を設定するだけ。
Reactの場合は、.jsx.tsxに変更するだけです。
.tsファイルもそのままimportできます。

App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue + Vite" />
</template>

<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { defineComponent } from 'vue'

// Composition APIなので defineComponent()を利用
export default defineComponent({
  name: 'App',
  components: {
    HelloWorld
  },
  setup() {
    return {}
  }
})
</script>

:warning: 注意点
2020/05/31現在、TypeScriptはJSへのトランスパイルのみ対応していて、型チェックは行えません。
基本的にエディタ上での型エラーの確認と、tsconfig.jsonを追加しビルド前にtsc --noEmitを利用して型チェックを行ってください。

PostCSS、Scssへの対応

Viteは、.vueファイルとインポートされた.cssファイルの全てにPostCSSを自動的に適用します。
必要なPostCSSのプラグインをnpmでインストールして、あとはpostcss.config.jsをプロジェクトルートに追加するだけです。

Autoprefixerの利用例。

$ npm i autoprefixer --save-dev
postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')()
  ]
}

CSS Pre-Processorsのscssも、sassをnpmインストールして、.vueファイルで<style lang="scss">を指定するだけで利用できます。

$ npm i sass --save-dev
<style lang="scss">
/* scss */
</style>

終わりに

とても高速に動作するのでViteかなり良いですね。あと、Vue 3.0のsandbox環境としても最高です。
今回紹介した以外にも機能盛り沢山なので、是非READMEを読んで欲しいです。
https://github.com/vitejs/vite

今後もさらに機能が拡張されるようなので引き続き動向を見ていきたいです。

最後に。Evan You氏のVite作成時の投稿がカッコ良すぎる。
こんなこといつか言ってみたいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[今度こそ完全に理解した]vueで数字しか入力できないinputタグのカスタムコンポーネントを作る

vue.jsで「数字」しか入力できないinput要素を作る
[Vue.js]小数や整数しか入力できないinputタグのカスタムコンポーネント

過去に二度、同じことを試行錯誤して記事を書きました
いずれも動作としては問題ないのですが今読み返すと「これってどうなの?」というポイントがあり
改めて考えて今度こそちゃんとした(つもりの)コードを書いてみました

結論

IntegerStringOnlyInput.vue
<template>
  <input
    type="text"
    :value="value"
    @input="sanitizeAndEmit($event.target.value)"
  >
</template>

<script>
export default {
  name: 'IntegerStringOnlyInput',
  props: {
    value: {
      type: String,
      required: true,
      default: '',
    },
  },
  watch: {
    // 親からの値変更の時もサニタイズするためにwatch
    value(newValue) {
      this.sanitizeAndEmit(newValue);
    },
  },
  mounted() {
    this.sanitizeAndEmit(this.value);
  },
  methods: {
    sanitizeAndEmit(val) {
      // 数字以外を消す & 全角数字を半角数字へ変換
      this.$emit(
        'input',
        val.replace(/[^0-90-9]/g, '').replace(/[0-9]/g, (s) => String.fromCharCode(s.charCodeAt(0) - 0xFEE0)),
      );
      // サニタイズされた値は直前の値と変わらないので再描画されない 強制的に再描画しないと、表示と実際の値がずれる
      this.$forceUpdate();
    },
  },
};
</script>

使い方としては普通のカスタムコンポーネントと同じで
importしてv-modelで変数をバインドするだけです

<IntegerStringOnlyInput v-model="int" />

過去の記事のコードがなぜよくなかったのか

vue.jsで「数字」しか入力できないinput要素を作る

v-model@input が同時に存在しているから

その記事では

<input
  @input="validate"
  v-model="numValue"
>

というように書いていました
幸か不幸かこのコードは意図した通りの挙動をしてくれます(通常のv-modelの挙動のあとに値を上書きする)

ではなぜ v-model@input が同時に存在するコードがだめなのか

そもそも v-model はシュガーシンタックス(※1)なので
つまるところ上記のコードは

<input
  @input="numValue = $event.target.value"
  @input="validate"
  :value="numValue"
>

と同じであると解釈できるのでは、と思いました
(@input の重複はパースエラーになるので実際にはこのように書くことはできませんが。。。)

そのため、たまたま動くだけで v-model@input が共存することは本来は意図されてない使われ方ではないかと考えています

  • もしそうならいつかバージョンが上がった時に動かなくなる可能性があるのではないか
  • そうでなくともコードとして同じイベントハンドラが2つあるという状況もあまりよいものではない

と思い、「これはよくない」という結論にいたりました

[Vue.js]小数や整数しか入力できないinputタグのカスタムコンポーネント

$refs を使っているから

値が変更されず再描画されないために実際の変数の中身と見た目がずれる問題を解消するために
当時は「再描画されないからずれる」ことに気づかず解決しようと $refs を使ってしまいましたが
そもそも $refs は使用が推奨されておらず(※2)、使わずに済むならそれに越したことはないため
今回気づいたずれの原因から再描画が解決の糸口なので $forceUpdate() の方がいいのではという結論に至りました。

単純にコードが冗長

computed を使ったために無駄にコード量が増え可読性が低下していました
改めて考えてみるとcomputed挟む必要はありませんでした

親コンポーネントからの変更にサニタイズがされない

親コンポーネントから数字以外のものが含まれる値に変更されてもその際には数字以外の文字が取り除かれないため
今回新たに watch で変更を監視してサニタイズされるようにしました

※1

※2

参考ドキュメント
- 特別な問題に対処する

好みや要件次第で

全角許容する場合は半角への変換をなくしたり
親からの変更時やmount時にサニタイズがされたくない場合は watchmounted を消したりでコントロール可能です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel + Vue.jsのかんたん実装

laravel上でVuejsを実装は、大まかに3段階です。

  1. resources/js/components にvueデータを作成。
  2. resources/js/app.jsにvueコンポーネントを定義。
  3. app.jsに定義したvueコンポーネントを resources/views/*.blade.phpにマークアップ。

スクリーンショット 2020-05-31 23.45.19.png

ExampleComponent.vueを表示してみましょう!
app.jsを以下のように設定します。

resources/js/app.js
require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue').default);

const app = new Vue({
    el: '#app',
});

デフォルトのwelcome.blade.phpに記述します。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>ExampleComponent</title>
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>
    <body>
        <div id="app">
            <example-component></example-component>
        </div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

vueファイルを記述、変更したらターミナルでコンパイルをします。

#コンパイル
npm run dev
#サーバーを立ち上げて確認
php artisan serve

表示されました!
スクリーンショット 2020-06-01 0.08.10.png

新しくVueを作成してみましょう。
1. vueファイルを作成します。
スクリーンショット 2020-06-01 0.12.15.png

resources/js/components/LaravelVue.vue
<template>
  <div>
    <h1>LaravelでVueを実装</h1>
    <p>Good!!</p>
  </div>
</template>
  1. resources/js/app.jsにvueコンポーネントを定義します。
resources/js/app.js
require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue').default);
Vue.component('laravelvue', require('./components/LaravelVue.vue').default);

const app = new Vue({
    el: '#app',
});
  1. app.jsに定義したvueコンポーネントを resources/views/welcome.blade.phpにマークアップ。
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>laravelvue</title>
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>
    <body>
        <div id="app">
            <laravelvue></laravelvue>
        </div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

コンパイルのたびにnpm run devするのは、面倒くさいので
vueファイルの記述が変更されるたびに自動でコンパイルしてくれるnpm run watch
を実行します。

#コンパイル
npm run watch

command + Tで新しくタブを作ってサーバーを立ち上げます。

#サーバーを立ち上げて確認
php artisan serve

表示されました!
スクリーンショット 2020-06-01 0.19.17.png

もう一度vueファイルの記述を変更してみましょう。

resources/js/components/LaravelVue.vue
<template>
  <div>
    <h1>LaravelでVueを実装できた!!</h1>
    <p>Yeah!!</p>
  </div>
</template>

npm run watchで監視されているので、vueファイルの記述に変更があると自動的にコンパイルをしてくれます。
ブラウザを更新してみましょう!
スクリーンショット 2020-06-01 0.32.47.png
更新されています。

かんたんでは、ありますが少しでも参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue/Nuxt】JSXでv-modalを使わずにデータを入力

VueでJSXを利用しているのですが、v-modelを使わずにデータを入力出来たのでメモがてら残します。

2種類の入力方法を検証できたため、2つに分けて書いていこうと思います。

v-modelを使わずにdataを直接書き換える

<script>
export default {
  render(h) {
    data = {
      email: ''
    }
    return (
      {h('input', {
        type: 'email',
        name: 'email',
        domProps: {
          value: this.email,
        },
        on: {
          input: (e) => {
            this.email = e.target.value
          },
        },
      })}
    )
  }
}
</script>

以下のドキュメントを参考にしました。

https://www.digitalocean.com/community/tutorials/vuejs-introduction-render-functions
https://jp.vuejs.org/v2/guide/render-function.html#v-model

入力用のメソッドを用意してあげる

ただ、上の書き方だと親コンポーネントから受け取ったpropsを直接書き換えてしまう実装のため、入力用のメソッドを用意してみました。

ContainerとPresenterでロジックとUIを分けているため、その書き方でサンプルコードを載せようと思います。

以下の記事を参考にしました。

https://github.com/vuejs/babel-plugin-transform-vue-jsx/issues/36

Container

container.js
import Presenter from './presenter'
const connect = (Presenter) => {
  return {
    name: `${Presenter.name}Container`,
    methods: {
      handleInput(name, value) {
        this[name] = value
      },
    },
    render(h) {
      const data = {
        email: '',
        password: '',
      }
      return h(Presenter, {
        props: {
          ...data,
          handleInput: this.handleInput,
        },
      })
    },
  }
}
export default connect(SignUpBody)

Presenter

presenter.vue
<script>
export default {
  name: 'Presenter',
  props: {
    handleInput: {
      type: Function,
      default: () => {},
    },
    email: {
      type: String,
      default: '',
    },
    password: {
      type: String,
      default: '',
    },
  },
  render(h) {
    return (
      <div>
        <form>
          {h('input', {
            type: 'email',
            name: 'email',
            domProps: {
              value: this.email,
            },
            on: {
              input: (e) => {
                this.handleInput('email', e.target.value)
              },
            },
          })}
          {h('input', {
            type: 'password',
            name: 'password',
            domProps: {
              value: this.password,
            },
            on: {
              input: (e) => {
                this.handleInput('password', e.target.value)
              },
            },
          })}
        </form>
      </div>
    )
  }
</script>

createElementを使いましたが別に以下のような書き方もすることができます。

<input
  type="email"
  name="email"
  value={this.email}
  onInput={(e) => {
    this.handleInput('email', e.target.value)
  }}
/>
<input
  type="password"
  name="password"
  value={this.password}
  onInput={(e) => {
    this.handleInput('password', e.target.value)
  }}
/>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue/Nuxt】JSXでspreadでいっぺんにpropsを渡す

VueでJSXを利用しているのですが、Reactみたいにpropsをいっぺんに渡せないかと思って検証しました。

実際にpropsを渡せたので、メモがてらサンプルコードを残します。

ContainerとPresenterでロジック部分とUI部分を分けているのですが、その書き方でそのまま共有させてください。

Container

container.js
import Presenter from './presenter'
const connect = (Presenter) => {
  return {
    name: `${Presenter.name}Container`,
    render(h) {
      const data = {
        email: '',
        password: '',
      }
      return h(Presenter, {
        props: {
          ...data,
        },
      })
    },
  }
}
export default connect(Presenter)

Presenter

presenter.vue
<script>
export default {
  name: 'Presenter',
  props: {
    email: {
      type: String,
      default: '',
    },
    password: {
      type: String,
      default: '',
    },
  },
  render() {
    return (
      <div>
        {this.email}
        {this.password}
      </div>
    )
  },
}
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue/最高にかっこいい】hoverすると画像全体がスーッと現れるCSSアニメーション実装

スクリーンショット 2020-05-31 21.55.38.png

Vueバージョン確認

npm list vue

まずは上記コマンドでバージョンの確認

twinzlabo@0.1.0 /Users/twinzlabo

── vue@2.6.11

画像一覧をhoverするとスーッと拡大されるアニメーションをコピペだけで実装

すでに上の方で確認してもらったかと思いますが、

特に変哲もない画像コンポーネントにスタイル修正を行うことで

画像をhoverするとスーッと画像の全体像が現れるクールなアニメーション実装をしていきましょう
スクリーンショット 2020-05-31 21.53.43.png

デフォルトの上の画像をhoverしたら下の画像のように画像の全体像が現れるアニメーションをカスタマイズしていきます
スクリーンショット 2020-05-31 21.53.53.png

デフォルト時は拡大して見えてるけどhoverするとその全体像が現れる

この感じかなりクールですよね

実装してみたら感動すること間違いなしです!

では早速コードをコピペしていきましょう

<template>
  <div class="images">
    <div class="image">
      <img src="https://images.unsplash.com/photo-1551607117-21fa129a211d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1720&q=80">
      <span>Hover this Image</span>
    </div>
  </div>
</template>
<style>
.images {
  display: flex;
  width: 100%;
  padding: 4% 2%;
  box-sizing: border-box;
  height: 60vh;
}

.image {
  flex: 1;
  overflow: hidden;
  transition: .5s;
  margin: 0 2%;
  box-shadow: 0 20px 30px rgba(0,0,0,.1);
  line-height: 0;
}

.image > img {
  width: 200%;
  height: calc(100% - 10vh);
  object-fit: cover;
  transition: .5s;
}

.image > span {
  font-size: 3.8vh;
  display: block;
  text-align: center;
  height: 10vh;
  line-height: 2.6;
}

.image:hover { flex: 1 1 50%; }
.image:hover > img {
  width: 100%;
  height: 100%;
}

</style>

画像をhoverするとスーッと画像の全体像が現れるクールなアニメーションが実装できましたか?

こういうの実装できるとめっちゃ興奮しますよね

下の記事では別の応用的で面白い画像のデザイン方法を掲載しているので是非挑戦してみてください

以上です

参考記事(この実装ができるとめっちゃ興奮しますよ)
【Vue/CSSアニメーション】画像一覧をhoverするとスーッと拡大されるアニメーションをコピペだけで実装
スクリーンショット 2020-05-31 21.36.02.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む