Vuex
について勉強してみて、ざっくりと理解できた内容についてまとめてみました。
間違いなどあればご指摘いただけると幸いです。
この記事でわかること
・Vuex
ってざっくりどんな仕組みになっているのか。
・使うことで何が嬉しいのか。
Vuex とは
ものすごくざっくり言うと、Vuexはアプリのデータを管理するための簡易的なDBみたいなものです。
極端に言うと単なる入れ物みたいなイメージだと私は考えています。
なので、やってることは
入れ物を用意して、そこに値を出し入れしているだけ
Vuexのメリット
データを1箇所で集中管理でき、ソースコードの可読性が向上、+デバックが容易になる。
Vuex
のメリットを語るには、Vuex
がない世界を想像すると理解しやすいです。
vue
では、コンポーネント間でprops
で親から子へデータを受け渡し、
$emit
で子から親にデータ送ることができます。
開発が進むことで、大体コンポーネントの種類も増えていきます。
コンポーネントが増えることで、当然ながらデータの受け渡しも増大します。
その結果、データがどこで変更されたか等のデータの流れが追いづらくなり、
メンテナンスが困難なコードとなってしまい困ります。( ;∀;)
このような問題を解決するために生まれたのがVuex
です。
Vuex 全体の流れ
Vuex
の中心にいるのが、storeと言われるアプリケーションのデータ状態を保持するコンテナで、
このstore
には4つの機能があります。(state, mutations, actions, getters
)
それぞれの役割についてみていきましょう。
※使用しているソースコードは公式から引用しています。
state
stateは、アプリケーション全体のデータの状態を管理します。
ここで管理したいデータを「名前: 初期値」みたいな形で定義します。
const store = createStore({
// 「名前: 初期値」 の形で定義する
state: {
count: 1
}
})
※ アプリケーションごとに1つしかストアは持たないことに注意が必要です。
mutations
mutationsは、stateの更新だけを行います。
muitationsには非同期処理を含んではいけないことに注意が必要です。
その理由は、非同期処理とstateの管理が混在すると、状態管理が追跡しにくくなるからです。
Vuex
のストアの状態を変更できる唯一の方法は、mutations
をコミット(実行)することです。
使い方は以下のように、Vuex
の状態(state)を第1引数として受け取り、状態の変更を行います。
const store = createStore({
state: {
count: 1
},
mutations: {
// ステート管理に必要なメソッドを用意する
increment (state) {
// 状態を変更する
state.count++
}
}
})
また、引数の指定も可能で、この追加の引数は、特定のミューテーションに対するペイロードと呼ばれます。
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
actions
actionsは、mutationsの呼び出しを行います。
また、actions自体は直接stateを変更できないことに注意が必要です。
また、非同期なAPI通信もここで行います。
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
// mutationsを呼び出している
context.commit('increment')
}
}
})
ここで登場したcontext
はコンテキストオブジェクトと呼ばれ、ストア配下にアクセスするために
以下のプロパティを提供します。
[Vuex] mutations, actions, gettersの引数について を参考にしました。
今回で言うと、contextオブジェクト
にあるcommit
を呼びだし、mutations
を実行しています。
また、渡されたcontextオブジェクト
からプロパティであるcommit
を取り出し、
同名の変数に渡すことで以下のように短縮して記述することもできます。
///
actions: {
increment ( { commit } ) {
commit('increment')
}
}
})
getters
gettersは、stateの値を算出したものを返す場合に役立ちます。
computed
と使い方はかなり近いです。
state
の内容を演算/加工処理をコンポーネント側で書くことできますが、
データの集中管理という観点でstore
の中に書いた方が利便性が向上します。
以下が具体的なコードです。
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos (state) {
return state.todos.filter(todo => todo.done == true)
}
}
})
getters
は第1引数として、state
を受け取ります。
filter
はjavascript
標準のメソッドで条件式(todo.done == true)
を満たす要素を取得します。
まとめ
上の図のように、データの流れが一方通行になっています。
非同期通信をactions
で行い、その後にmutations
を実行することでstate
を変更している。
state
が変更されたことを検知し、getters
でstate
のデータを再取得して、画面に反映させる仕組みです。
最後に
説明していてなんですが、Vuex
で管理されるデータはどこからでも参照、変更できる
グローバル変数であるため、使いどころも難しいと感じます。
影響範囲が大きい分、意識しないといけないものが増えるので、読むときや修正する際に
精神的な負担が高いコードになってしまう危険性もあるみたいです。
ここら辺はトレードオフで考えなければいけないんだなぁと。