====== 15.Vue.jsのvuexでカウンター ====== vuexをなんとかして理解するために簡単なアプリを写経しています。前回の[[14.vue.jsのvuexで入力フォーム:index.html|14.vue.jsのvuexで入力フォーム]]では、文字を入力すると、そのまま別のコンポーネントで表示するアプリを、vuexを用いて作成しました。 今回は、『カウンターアプリ』を作成しながら、vuexの理解を深めたいと思います。 ===== 初めてのVuex目次 ===== [[14.vue.jsのvuexで入力フォーム:index.html|14.vue.jsのvuexで入力フォーム]] [[15.vue.jsのvuexでカウンター:index.html|15.Vue.jsのvuexでカウンター]] [[16.vue.jsのvuexでパスワード制限:index.html|16.vue.jsのvuexでパスワード制限]] [[17.nuxt.jsのvuexでパスワード制限:index.html|17.Nuxt.jsのvuexでパスワード制限]] ===== すること ===== -vuexを用いてカウンターアプリを作成する -数字が表示されている(App.vueに表示させる) -『+1』を押すと、数字が1増える(components/counter.vueで『+1』ボタンを表示する) -『-1』を押すと、数字が1減る(components/counter.vueで『-1』ボタンを表示する) 今回は、https://qiita.com/ferretdayo/items/edcd2b6682148e477104を写経させていてだきました。 ソースコード https://github.com/adash333/Vue_Vuex_Counter https://codesandbox.io/s/l556xq6327 Demo https://l556xq6327.codesandbox.io/ {{:15.vue.jsのvuexでカウンター:pasted:20190126-204253.png}} ===== 開発環境 ===== Chrome Codesandbox Vue 2.5.2 Vuetify 1.4.2 Vuex 3.1.0 ===== CodeSandboxで新規Vue.jsアプリを作成 ===== 最初に、CodeSandbox上で、新規Vue.jsアプリを作成し、CSSフレームワークVuetifyが利用できるように設定します。 -https://codesandbox.io/へ行き、画面右上の、『Create Sandbox』をクリック -Vue をクリック -Add Dependencyから、Vuetifyをインストール -index.htmlの編集 -src/main.jsの編集 {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-123704.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-123723.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-123749.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-123804.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-123835.png}} index.htmlと、src/main.jsは、以下をコピペします。 {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-124228.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-124252.png}} 以上で、Vue.jsで、Vuetifyを利用する準備ができました。 ===== Vuetifyのテンプレートを適用 ===== src/App.vueに、以下のコードをコピペします。 {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-124906.png}} これで、App.vueの土台が出来上がりました。 ===== Vue.jsにVuexをインストール ===== Vuexを利用するための準備を行います。 -Add Dependencyから、Vuexをインストール -Add Dependencyから、babel-polyfillをインストール -src/main.jsの編集 -src/store.jsの新規作成 {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-141702.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-141741.png}} {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-141941.png}} src/main.jsは、以下の2行を追加します。(追加する位置には注意する必要があります。) import store from './store' store, (変更前) {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-142758.png}} (変更後) {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-142928.png}} この時点では、store.jsを作成していないので、エラーが出ます。 *src/store.jsのテンプレートの作成 src/store.jsを新規作成し、以下をコピペします。 import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} }); export default store; これでも、まだエラーが出ています。。。 {{:14.vue.jsのvuexで入力フォーム:pasted:20190121-143709.png}} 右側の画面の更新画面を押すと、以下のように、vuexが動く準備ができたようです。 {{:15.vue.jsのvuexでカウンター:pasted:20190126-021146.png}} ===== Counterコンポーネントを作成し,カウンター機能の追加 ===== まずは、vuexを用いない方法で、カウンター機能を作成します。 -カウンターの表示、カウンターの『+1』、『-1』のボタンをcomponents/counter.vueに設置 でいってみたいと思います。 *src/components/Counter.vueの新規作成 {{:15.vue.jsのvuexでカウンター:pasted:20190126-033930.png}} *HelloWorldコンポーネントでCounterコンポーネントを表示 src/App.vueの変更 (変更前) {{:15.vue.jsのvuexでカウンター:pasted:20190126-034149.png}} (変更後) {{:15.vue.jsのvuexでカウンター:pasted:20190126-034431.png}} App.vueとcomponents/Counter.vueを少しだけ変更して、以下のようになった。 {{:15.vue.jsのvuexでカウンター:pasted:20190126-035002.png}} +1をクリックと数字が1増えて、-1をクリックすると、数字が1減る。 {{:15.vue.jsのvuexでカウンター:pasted:20190126-035108.png}} 次は、上記のカウンターの増減を、Vuexを用いる方法に書き換えていきます。 ===== Vuexの説明 ===== Vuexは図のようなデータの流れを持つ,状態管理のパターンです. -ComponentはdispatchすることでActionsを実行する -ActionsはCommitすることでMutationsを利用する -MutationsはStateを変更する {{:15.vue.jsのvuexでカウンター:pasted:20190126-035541.png}} Vuexの図:https://vuex.vuejs.org/ja/より引用 これだけでは何のことかさっぱりわかりません。。。 しかし、アプリが複雑になるほど、Vuexは必要になることは間違いないらしいので、やるしかありません! (Vuexを用いないでデータをやり取りするためには、親コンポーネントと子コンポーネントの間でpropsと$emitのデータのバケツリレーが必要いなるためらしいです。) 今度こそ、上記のカウンターアプリをVuexを用いたアプリにします。 ===== カウンター機能にVuexを利用 ===== 以下の3つを行う必要があるそうです。(参考:[[https://qiita.com/ferretdayo/items/edcd2b6682148e477104#%E3%82%AB%E3%82%A6%E3%83%B3%E3%82%BF%E3%83%BC%E6%A9%9F%E8%83%BD%E3%81%ABvuex%E3%82%92%E5%88%A9%E7%94%A8|カウンター機能にVuexを利用]]) -src/store.jsファイルを新規作成し,state, actions, mutations, gettersを定義 (済み) -src/main.jsにstoreファイルに書かれたstoreを登録 (済み) -CounterコンポーネントからVuexを利用 ==== src/store.jsファイルの編集 ==== stateとかactionsとか言葉がたくさん出てきますが、以下を、100回くらい声に出して読んで、写経していくしかなさそうです。。。(参考:[[https://qiita.com/ferretdayo/items/edcd2b6682148e477104#%E3%82%AB%E3%82%A6%E3%83%B3%E3%82%BF%E3%83%BC%E6%A9%9F%E8%83%BD%E3%81%ABvuex%E3%82%92%E5%88%A9%E7%94%A8|カウンター機能にVuexを利用]]) -stateには状態を持ちたい情報を定義する -actionsでアクションを定義,commitすることでmutationsが実行される -gettersはコンポーネントがデータを取得する際に利用される -mutationsはstateの状態を変更する際に利用される -gettersとmutationsのメソッドの引数には定義したstateが代入される -actionsのメソッドの引数のcontextにはdispatch,commit,getters,stateを持つオブジェクトが代入される -引数に{ commit }とすると,commitだけ利用できる. src/store.jsの変更 (変更前) {{:15.vue.jsのvuexでカウンター:pasted:20190126-040721.png}} (変更後) {{:15.vue.jsのvuexでカウンター:pasted:20190126-041118.png}} actionsのメソッドの引数は、commitの場合は、上記の通り、2通りの書き方があるそうです。 (contextの中にcommitがあるだけですが。) const actions = { increment(context) { context.commit("increment"); }, decrement({ commit }) { commit("decrement"); } }; ==== src/main.jsにstoreを登録 ==== 上の方で、登録済みです。 import store from "./store"; // new Vue の中に、 store, ==== CounterコンポーネントからVuexの利用 ==== src/main.jsで追記したstoreを利用するにはthis.$storeから利用するとのことです。(参考:[[https://qiita.com/ferretdayo/items/edcd2b6682148e477104#counter%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%8B%E3%82%89vuex%E3%81%AE%E5%88%A9%E7%94%A8|CounterコンポーネントからVuexの利用]]) 上記サイトを写経しているだけで、詳しいことはよくわからないのですが、とにかく、$this.store.stateでstateをゲットしてくるらしいです。。。 *state :stateにアクセスする際に利用 *dispatch :actionsで定義したメソッド名を引数に渡すことで,actionを実行できる *getters :ここからgettersで定義したメソッドを利用できる *commit :mutationsに定義したメソッド名を引数に渡すことで,mutationを実行できる 今回のカウンターアプリでは、store.jsからのデータの取得にはcomputedで,storeのgettersで定義したcountの値を返すgetCountメソッドを利用します。 computed: { count () { return this.$store.getters.getCount } }, src/components/Counter.vue (変更前) {{:15.vue.jsのvuexでカウンター:pasted:20190126-055107.png}} (変更後) {{:15.vue.jsのvuexでカウンター:pasted:20190126-055347.png}} カウンターの初期値や、+1、-1の状態管理をvuexに移動しても、ちゃんと、カウンターが動くことが確認できました。 {{:15.vue.jsのvuexでカウンター:pasted:20190126-055912.png}} ここまでのソースコード(CodeSandeboxバージョン) {{ :15.vue.jsのvuexでカウンター:vue-vuex-counter.zip |}} ===== mapGetters, mapActionsを用いてVuexのコードを少し発展させる ===== mapState, mapGetters, mapMutations, mapActionsといったヘルパーと、スプレッド演算子『...』を用いると、記載するコード量を減らすことができます。 (いきなりこれを見ると、『わけわからん状態』ですが、上記の経過をたどった後であれば、何となく分かった気になれる気がします。) import { mapState, mapGetters, mapMutations, mapActions } from 'vuex' stateとgetterは算出プロパティcomputedに登録します。 mutationとactionはメソッドmethodsに登録します。 computed: { // ローカルの算出プロパティ myProperty() { ... }, ...mapGetters([ // this.messageをstore.getters.messageにマッピング 'message' ]), // メソッド名を変更したい場合はオブジェクトで定義 ...mapGetters({ // this.messageAliasをstore.getters.messageにマッピング messageAlias: 'message' }) } カッコが、[]なのか、{}なのか、紛らわしいです。[]は配列、{}はオブジェクトなのだと思います。 組み合わせる必要がなければ、以下のように、オプションに直接、登録することもできるとのことです。 (しかし、これは汎用的ではないと思われます。) computed: mapGetters(['message']), methods: mapActions(['add', 'update', 'remove']) 上記のカウンターアプリを、mapGetters, mapActionsを用いて書き換えると、以下のようになります。 import { mapGetters, mapActions } from 'vuex' export default { name: 'Counter', computed: { count () { return this.$store.getters.getCount } }, methods: { ...mapActions([ 'increment', 'decrement' ]) } } (変更前) {{:15.vue.jsのvuexでカウンター:pasted:20190126-202927.png}} (変更後) {{:15.vue.jsのvuexでカウンター:pasted:20190126-203626.png}} 今回のアプリ作成はここまでとなります。 ===== Vuexでカウンターアプリのリンク ===== https://qiita.com/ferretdayo/items/edcd2b6682148e477104 @ferretdayo 2017年12月23日に更新 IDOM Engineer Advent Calendar 201723日目 カウンターを作りながら,VueとVuexの基本をやる →最初はこちらを写経でしょうか? -Vuexを利用しないでカウンターアプリを作成 -Vuexを利用したカウンターアプリに変更 -Vuexのコードを発展させる(mapActions,mapState,mapGetters,mapMutationsの利用) https://qiita.com/nasum/items/d17c0a628e6c32616b85 @nasum 2017年12月17日に更新 Vue.js #4 Advent Calendar 201717日目 改めて学び直すVuex →カウンターアプリを作成します。 https://qiita.com/d-dai/items/7df318b9369be3d58a58 @d-dai 2018年11月25日に更新 Vuexの簡単なサンプルを作ってみた →カウンターアプリを作成します。 ===== ソースコード ===== ソースコード https://codesandbox.io/s/l556xq6327 DEMO https://l556xq6327.codesandbox.io/ ===== リンク ===== 目次:[[:index.html|このサイトについて]] 前:[[14.vue.jsのvuexで入力フォーム:index.html|14.vue.jsのvuexで入力フォーム]] 次:[[16.vue.jsのvuexでパスワード制限:index.html|16.vue.jsのvuexでパスワード制限]]