基礎からメモ: Vue.js CH8-p264 コンポーネントでストアを使う
コンポーネントでストアを使う
親子間コンポーネントで状態管理する例
まずsrc/store.js
を定義
[src/store.js]
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { message: '初期メッセージ' }, getters: { message(state) { return state.message } }, mutations: { setMessage(state, payload) { state.message = payload.message } }, actions: { doUpdate({ commit }, message) { commit('setMessage', { message }) } } }) export default store
src/main.js
からstore.js
を読み込みアプリケーションにストアを登録
[src/main.js]
import Vue from 'vue' import App from './App.vue' import store from '@/store.js' Vue.config.productionTip = false new Vue({ el: '#app', store, render: h => h(App) })
App.vue
に、ローカルの message とストアの message を同期させる算出プロパティを定義し、message を変更するための子コンポーネントEditForm
のカスタムタグを記述
[src/App.vue]
<template> <div id="app"> <p>{{ message }}</p> <EditForm/> </div> </template> <script> import EditForm from '@/components/EditForm.vue' export default { name: 'App', components: { EditForm }, computed: { message() { return this.$store.getters.message } } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
子コンポーネントのEditForm
[src/components/EditForm.vue]
<template> <div class="edit-form"> <input type="text" ref="input" :value="message" @input="doUpdate"> </div> </template> <script> export default { name: 'EditForm', computed: { message() { return this.$store.getters.message } }, methods: { doUpdate() { this.$store.dispatch('doUpdate', this.$refs.input.value ) } } } </script>
これで Appコンポーネントに描かれたメッセージと、EditFormコンポーネントで描画されたとインプットフォームへの入力がリアルタイムで同期するようになる。流れをまとめると、
store.js
に登録されたストアは state に messageデータを保持している。
main.js
からそのstore.js
を読み込み、Appコンポーネントを使う↓
Appコンポーネントは、EditForm子コンポーネントを読み込む
↓
App は computed を使って、ストアのゲッター経由で初期メッセージを描画
EditFormのインプットフォームにも同じ方法で初期メッセージを描画
↓
インプットフォームに何かが入力されると、EditFormコンポーネントの doUpdateメソッド が呼び出され、 doUpdateメソッドはストアの同名メソッドをディスパッチ(機能の呼び出し)をする。そのさいフォーム入力されたデータを渡す。
↓
ストアのdoUpdateメソッドは mutations である setMessageメソッドを使って state の messageデータを書き換えることで、フォームとその上の表示が同期される。
何段階もあって一見複雑だが、メインJSとそこからつながる親子コンポーネント間でストアのデータが共有されていること、結果的にストアのデータはミューテーションを経由してのみ変更されているのがわかる。(ステートはミューテーション以外から変更してはいけないというルール)
ステートやゲッターに v-model を使う
フォームにv-model
を使うと、フォームに紐付けられたデータを自動的に更新するようになる。ステートの場合はミューテーション以外から書き換えてはいけないルールなのでこれはエラーになる。だから先の例ではフォームのv-on
からイベントハンドラを呼び出し、そこからディスパッチでミューテーションを呼び出すという方法をとった。
ただし、算出プロパティのセッターからディスパッチを使ってミューテーションを間接的に呼び出すという書き方もできる。これだとv-model
からデータの自動更新がされる際にデータに定義されたセッターが自動的に呼び出されるので、よりシンプルに書ける。
[src/components/EditForm.vue]
<template> <div class="edit-form"> <input type="text" v-model="message"> </div> </template> <script> export default { name: 'EditForm', computed: { message: { get() { return this.$store.getters.message }, set(value) { this.$store.dispatch('doUpdate', value) } } } } </script>
これだとインプットフォームへの入力は"message"データの内容としてv-model
で紐付けられているので、"message"データのセッターに渡される引数はそのフォームへの入力内容を自動的に表し、this.$refs.input.value
と書かずに単にvalue
だけで済む。