非同期処理を実行できるアクション
前述の通り、Vuexではミューテーションに非同期処理を含むことができないので、ステートの更新に非同期処理が必要な場合は、コンポーネントからアクションを実行して、その中でミューテーションを実行するようにします。実装例をリスト8に示します。
export const actions = {
// setNameミューテーションを実行 ...(1)
doSetName(context, name) {
context.commit('setName', name)
},
// setPhysicalミューテーションを実行 ...(2)
doSetPhysical({ commit }, payload) {
commit('setPhysical', payload)
},
// setPhysicalミューテーションを遅延実行 ...(3)
doSetPhysicalDelayed({ commit }, payload) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
commit('setPhysical', payload)
resolve(payload)
}, 1000)
})
}
}
(1)はsetNameミューテーションを実行するアクションで、第1引数に渡されるcontextのcommitメソッドでsetNameミューテーションを実行(commit)します。同様に(2)はsetPhysicalミューテーションを実行します(引数の「{ commit }」記法については後述の補足を参照)。(3)は非同期処理で、setPhysicalミューテーションを1秒後に実行します。Promiseを返却すると、呼び出し元で処理完了を検知できます(詳細は後述します)。
Vuexストアを利用するコンポーネントでは、this.$store.dispatchメソッドでアクションを実行(Dispatch)できます。第1引数にアクション名、第2引数にアクションに渡す引数を設定します。
this.$store.dispatch('doSetName', this.name)
また、リスト8(3)の通りアクションでPromiseを返却すると、コンポーネントでPromiseを受け取って、アクション実行完了後の処理を実装できます。
this.$store.dispatch('doSetPhysicalDelayed', { // アクション実行
height: this.height,
weight: this.weight
})
.then(payload => { // アクション実行完了後の処理 ...(1)
this.payload = JSON.stringify(payload, null, ' ')
})
リスト10では、入力して1秒経過後にVuexストアに変更が反映されるとともに、アクション実行完了後の処理(1)で、アクションに与えたパラメーターが画面表示されます。実装の詳細はサンプルコード(p003-action)を参照してください。
[補足]アクションの記述を簡略化できる「引数分割束縛」
リスト8(2)の、引数を中カッコで囲んだ「{ commit }」記述は、Vuexの公式ページで「引数分割束縛(argument destructuring)」と説明されています。関数の引数に渡されるオブジェクトから、中カッコ内のプロパティやメソッド(ここでは「commit」メソッド)を取り出して、関数内で利用できるようにします。引数分割束縛を利用して関数内の記述を簡略化できます(リスト11)。
doSetPhysical(context, payload) {
context.commit('setPhysical', payload) // ...(1)
}
↓
// 第1引数に渡されるオブジェクトからcommitメソッドを取り出す
doSetPhysical({ commit }, payload) {
commit('setPhysical', payload) // このcommitは(1)のcontext.commitに相当
}
引数分割束縛では、メソッドの引数に「分割代入」を行っています。分割代入の詳細はMozillaのリファレンスを参照してください。
複雑なVuexストアをモジュールに分割
Webページの機能が増えていくと、Vuexストアの内容が複雑になっていきます。こういった場合、Vuexストアをモジュールに分割できます。実装方法を図3のサンプルで説明します。これまでの健康診断のサンプルに、血液検査の結果を入力できるInputBloodコンポーネントを追加し、基本情報(InputBasicコンポーネント)にIDの項目を追加しました。
Vuexストアをモジュールに分割するには、storeフォルダー配下にindex.js以外のJavaScriptファイルを配置して、それぞれにVuexストアを実装します。このサンプルでは、基本情報(IDと氏名)をindex.js、身体測定の情報(身長と体重)をphysical.js、血液検査の情報(肝臓のγ-GT)をblood.jsでそれぞれ管理します。
blood.jsの内容をリスト12に示します。γ-GTを保持するステート(1)と、ステートを更新するミューテーション(2)、γ-GTが正常値かどうかを判定するゲッター(3)、ミューテーションを呼び出すアクション(4)を実装しています。blood.js以外のVuexストアは、サンプルコードを参照してください。
// ステート ...(1)
export const state = () => ({
gammaGT: 0 // 肝臓のγ-GT
})
// ミューテーション ...(2)
export const mutations = {
setValue(state, payload) {
state.gammaGT = payload.gammaGT;
}
}
// ゲッター ...(3)
export const getters = {
isGammaGTNormal(state) {
return state.gammaGT <= 70 // γ-GTの正常値:70以下
}
}
// アクション ...(4)
export const actions = {
doSetValue({ commit }, payload) {
commit('setValue', payload)
}
}
モジュールに分割された(index.js以外の)Vuexストアのステートやゲッターを参照するには、リスト13の通り、Vuexストアのモジュール名(「blood」)を設定します。
γ-GT:{{ $store.state.blood.gammaGT }}
→{{ $store.getters["blood/isGammaGTNormal"] ? "正常値" : "異常値" }}です。
また、モジュールに分割されたVuexストアのアクションやミューテーションを実行するには、第1引数に設定する名前を「<モジュール名>/<メソッド名>」とします。
this.$store.dispatch('blood/doSetValue', {
gammaGT: this.gammaGT
})
まとめ
本記事では、Vue.jsで状態管理に用いるVuexストアを、Nuxt.jsで利用する方法を説明しました。Vuexストアを利用することで、Webページ全体の状態を一括管理して、複数のコンポーネントから不整合なく参照・更新できます。
次回は、HTTPクライアントライブラリAxiosを利用して、コンポーネントやVuexストアのデータを非同期取得する、Nuxt.jsの非同期処理について説明します。
