はじめに
前の記事ではVuexを理解するための前提知識(状態管理やデータフローについて)を見てきました。
本記事ではいよいよ、Vuex本体について入っていきます。
この記事を読むと
- Vuexの構成要素やその使い方が分かる
- Vuexを用いた本格的なアプリケーション開発に向けた準備が整う
想定読者
- Vue.js や Nuxt.js の初級〜中級者
- Vuex を何となく雰囲気で使っている
前提知識
- JavaScript 及び Vue についての基本知識 (Vue の基本知識がない方はVue.js&Nuxt.js超入門が入門書として最も最適です。)
- 状態管理やデータフローについての基礎知識)
Vuex による状態管理
いよいよ Vuex に入って行きます。
Vuex は Vue アプリケーション向けの状態管理ライブラリです。
Vuexは単なるライブラリとして機能を提供するだけではなく、公式に Vuex を使う際の実装のルールも示しており、それを含めてVuexです。
よって、例えば状態の更新はミューテーションでのみ行われるため、更新処理を探したいときはミューテーションを探せば良いです。
これには複数人で開発する際も既存のルールに従うだけで良いため設計やコミュニケーションの手間も省けます。
ストア
ストアは主にアプリの状態を保持する役割を担います。
その他にも状態管理に関する機能を盛り込んでおり、vuex の根幹となります。
// ストアの作成と代入
cosnt store = new Vuex.Store({オプション})
Vuexは信頼できる唯一の情報源であることを前提に設計されています。
アプリケーション内で常にただ一つのストアのみが存在するようにします。※
※一つのディレクトリという意味であり、必ずしもファイスが一つとは限りません。例えばNuxt.jsのモジュールモードにおいてはストア内に複数のJSファイルが存在します。詳しくはこの記事の後半、および次の記事で扱います。
ストアの構成要素
ストアの構成要素として、以下の4つの概念が存在します。
- アプリのステート(State)
- ステートの一部や、ステートから計算された値を返すゲッター(Getter)
- ステートを更新するミューテーション(Mutation)
- 主にAjaxリクエストのような非同期処理や、LocalStorageへの読み書きのような外部APIとのやり取りを行うアクション(Action)
ステートは状態、ミューテーションは更新処理に対応します。
規模の大きいアプリを作る際には、上記4つの構成要素をモジュール(Module)という単位で分割して見通しを良くします。
アプリの状態を全て一つの場所に置いてしまうと逆に管理が大変になるのではないかと感じるかもしれませんが、モジュール※を使うことで、信頼できる唯一の情報源を守りながら状態やそれに関わる更新、取得のロジックを複数の単位に分割をし、管理をシンプルに行えます。
ステート
ステートの概要
Vuexのステートはアプリ全体の状態を保持するオブジェクトです。
全てのステートは一つの木構造として表現されます。
アプリケーションの全ての状態を一つの木としてステートに保持することで、「信頼できる唯一の情報源」として機能します。
しかし、アプリケーションの全ての状態を必ずしもVuexで管理するべきではありません。Vuexのステートで管理するべきデータと、そうでないデータの例は下記の通りです。
ステートに適したデータ
- ログイン中のユーザーの情報など、アプリ全体で使用されるデータ
- ecサイトにおける商品の情報など、アプリの複数の場所で使用される可能性のあるデータ
コンポーネント側で持つべきデータ
- マウスポインタがある要素の上に存在するかどうかを、表すフラグ
- ドラッグ中の要素の座標
- 入力中のフォームの値
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// ストアの定義
const store = new Vuex.Store({
state: {
count: 10
}
ステートはコンポーネントのdataオプションに渡された値と同じように変更が追跡されます。
ステートに対して何らかの更新を行うとその変更は自動的にコンポーネントの算出プロパティやテンプレートへと反映されます。
これはVuexが内部的にVueのリアクティブシステムを活用して実装されているためです。
また、ステート内の依存関係がリアクティブシステムによって計算されるため、ステート更新時のuiの再描画が必要最小限になるというメリットもあります。
ゲッター
ゲッターの概要
ゲッターはステートから別の値を算出するために用いられます。例えばユーザーの操作によって商品のリストを絞り込みたい時にはゲッターで絞り込んだ商品のリストを算出します。
ゲッターを使用することでコンポーネント上で表示のためにステートを計算することが避けられ、異なるコンポーネント間でロジックを再利用できるようになります。
ゲッターの定義方法
gettersオプションに関数をもつオブジェクトを指定することでゲッターを定義します。
コンポーネントの算出プロパティとよく似た機能ですが、引数にステートと他のゲッターが渡され、それらを使った値を返す点が異なります。
ゲッターの使い方
ゲッターはstore.gettersから参照できます。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// ストアの定義
const store = new Vuex.Store({
state: {
count: 10
},
// gettersオプションでgettersを定義する
getters: {
// ステートから別の値を算出する
squared: (state) => state.count* getters.squared
}
})
ゲッターはコンポーネントのcomputedオプションと同様に評価された値がキャッシュされます。
キャッシュされた値はそのゲッターが依存しているステートが更新されない限り再評価されません。
したがって、よく使用するステートの算出ロジックはゲッターにすることでパフォーマンスの向上が期待できます。一方でゲッターを参照したときに定義した関数が常に実行されるわけではありません。
例えば依存するステートが存在しない時にサーバーから値を取得するというような処理はゲッターの中には書かず、続けて解説するミューテーション、アクションを使って取得とステートへの反映を行います。
ミューテーション
ミューテーションの概要
ミューテーションはステートを更新するために用いられます。
Vuexでは規約としてミューテーション以外がステートの更新を行うことを禁止しています。
ステートの更新をミューテーションのみが行えば、ステートの変更がいつどこで発生したのかを追跡しやすくなります。
ミューテーションの定義方法
mutationsオプションにミューテーション名をキーに持ち、ハンドラー関数を値に持つオブジェクトを指定することでミューテーションを定義できます。
ハンドラー内では第一引数に渡されたステートを更新します。
ミューテーションの使い方
ミューテーションは直接は呼び出せません。store.commitにミューテーション名を与えて呼び出します。
これはイベントの発生と監視によく似ています。
incrementというイベントが発生したときに、その名前で登録したミューテーションハンドラーが実行されると考えると分かりやすいです。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// ストアの定義
const store = new Vuex.Store({
state: {
count: 10
},
// gettersオプションでgettersを定義する
getters: {
// ステートから別の値を算出する
squared: (state) => state.count* getters.squared
},
mutations: {
// 'incremation'ミューテーションを定義
increment(state) {
state.count = state.count + 1
}
}
})
store.commitの第二引数になんらかの値を与えるとそれがハンドラーの第二引数に渡されます。この値のことをペイロード(payload)※と呼びます。ペイロードを使用することで、同じミューテーションでも渡す値によって異なるステートに更新できます。
※payloadの意味は?
ミューテーション内で行う処理は非同期を用いると意図しない動作を引き起こす可能性があるため、全て同期的にする必要があります。
非同期処理を行う必要があるときは次に紹介するアクションを代わりに使用します。
アクション
アクションの概要
アクションは非同期処理や外部APIとの通信を行い、最終的にミューテーションを呼びだすために用いられます。
アクションの定義方法
actionsオプションにアクション名をキーに持ち、ハンドラー関数を値に持つオブジェクトを指定することでアクションを定義します。
アクションの使い方
アクションはミューテーションと同様に直接呼び出すことはできません。store.dispatchにアクション名を渡して呼び出します。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// ストアの定義
const store = new Vuex.Store({
state: {
count: 10
},
// gettersオプションでgettersを定義する
getters: {
// ステートから別の値を算出する
squared: (state) => state.count* getters.squared
},
mutations: {
// 'incremation'ミューテーションを定義
increment(state) {
state.count = state.count + 1
}
},
// acitionsオプションでアクションを定義する
actions: {
incrementAction(ctx) {
// incrementミューテーションを実行する
ctx.commit('increment')
}
})
アクションの定義はミューテーションとよく似ています。ただし、ハンドラーの第一引数にステートではなくコンテキスト(context)と呼ばれる特別なオブジェクトが渡される点で異なります。
コンテキストには以下が含まれます。
- state: 現在のステート
- getters: 定義されているゲッター
- dispatch: 他のアクションを実行するメソッド
- commit: ミューテーションを実行するメソッド
stateやgettersは、例えばデータのロード中にはアクションの処理を行わないというような現在の状態に応じてアクションの処理を切り替えるときに使います。
dispatchを使うことで、すでに定義してある他のアクションを呼びだせます。※
これによって共通の処理を一つのアクションにまとめることができますが、使い過ぎるとどのアクションから呼ばれているのか分かりづらくなるので気をつけましょう。
アクションはミューテーションを実行するのに用いられるため、commitが使われることが最も多いでしょう。
以下はAjaxでデータを取得し、そのデータをペイロードに含めたミューテーションを呼び出すアクションを定義している例です。第一引数のコンテキストを分割代入({ commit })することで短い記法で書かれています。また、ミューテーションと同様にアクションも第二引数にペイロードを受け取ります。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// ストアの定義
const store = new Vuex.Store({
state: {
count: 10
},
// gettersオプションでgettersを定義する
getters: {
// ステートから別の値を算出する
squared: (state) => state.count* getters.squared
},
mutations: {
// 'incremation'ミューテーションを定義
increment(state) {
state.count = state.count + 1
}
},
// acitionsオプションでアクションを定義する
actions: {
incrementAction(ctx) {
// incrementミューテーションを実行する
ctx.commit('increment')
}
})
おわりに
如何だったでしょうか。簡単に復習してみましょう。
- Vuexはステート、ゲッター、ミューテーション、アクションの4つの要素で構成される
- それぞれの要素の定義方法と使い方を覚える
この内容がバッチリでしたら、Vuexの基本的な使い方が頭に入ったことになります。
次の記事では、さらにより実践的な応用知識について見ていきます。例えばNuxt.jsにおけるVuexの扱いについてです。
参考
初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発
Web制作者のためのCSS設計の教科書 モダンWeb開発に欠かせない「修正しやすいCSS」の設計手法
Vue.js&Nuxt.js超入門
Vue.js入門 基礎から実践アプリケーション開発まで
21Stepで体得 Vue.jsハンズオン
Hello!! Nuxt.js Hello!!Vue&Nuxtシリーズ
Nuxt.jsビギナーズガイド
Nuxt.jsとFirebaseを使って爆速で何か作る前に読む本
実践Firestore
Nuxt.js - Vue.js on Steroids【Udemy】
超Vue.js 2 完全パック - もう他の教材は買わなくてOK! (Vue Router, Vuex含む)【Udemy】
Nuxt JS入門決定版!Vue.jsのフレームワークNuxt JSの基本からFirebaseと連携したSPAの開発まで【Udemy】