Vuex は Vue 向けの集中型 state 管理パターンとライブラリです。Vue 2 の既存アプリケーションでは、認証情報、カート、画面横断の設定などを Vuex Store で管理していることが多くあります。
新規の Vue プロジェクトでは Pinia がよく選ばれますが、Vuex を理解すると既存コードを安全に読めます。重要なのは API 名を覚えることより、state がどの経路で変わるかを追えることです。
Vuex の一方向の流れ
Vuex は主に state、getters、mutations、actions で構成されます。
コンポーネント → dispatch → action → commit → mutation → state → 画面
- state: アプリケーションが保持する事実
- getters: state から導く値
- mutations: state を同期的に変更する唯一の場所
- actions: 非同期処理や複数 mutation の調整を担当する場所
この分離により、Devtools でどの mutation が state を変えたかを追跡しやすくなります。
Store の基本例
次はカウンターを持つ最小の Store です。通信などの非同期処理は action に置き、mutation は state の変更だけを扱います。
export default new Vuex.Store({
state: {
count: 0,
},
getters: {
doubleCount: (state) => state.count * 2,
},
mutations: {
increment(state) {
state.count += 1
},
},
actions: {
async refreshCount({ commit }) {
const count = await fetchCount()
commit('setCount', count)
},
},
})
例では refreshCount が非同期処理を担当し、結果を mutation 経由で state に反映します。mutation の外側で state を直接変更すると、変更履歴を追いにくくなります。
コンポーネントから使う
Options API のコンポーネントでは、this.$store.state、this.$store.getters、this.$store.commit、this.$store.dispatch を使えます。規模が大きくなると、mapState や mapActions などのヘルパーで記述を整理します。
export default {
computed: {
count() {
return this.$store.state.count
},
},
methods: {
increment() {
this.$store.commit('increment')
},
},
}
コンポーネントが Store の内部構造を多く知りすぎないよう、意味を持つ action や getter を用意します。たとえば commit('SET_USER', user) を画面中へ散らすより、認証に関する action に集約した方が変更しやすくなります。
Module で責務を分ける
一つの root Store にすべてを置くと、state と mutation の名前が増え続けます。auth、cart、notifications のように責務ごとの module に分け、必要に応じて namespace を使います。
ただし module を増やすほど、他 module への依存や root state へのアクセスも複雑になります。画面構造ではなく、データの所有者と変更理由を基準に分割します。
Vuex を保守・移行するときの判断
Vuex があるプロジェクトでは、まず mutation と action をたどり、state の正本を確認します。いきなり Pinia へ書き換えるより、Store の責務、非同期処理、テスト、Devtools の利用状況を把握する方が安全です。
新規機能を追加する際は、プロジェクトが Vue 2 を継続保守するのか、Vue 3 と Pinia へ移行する計画があるのかを確認します。Vuex の知識はレガシーコードを読み解く基礎として、Pinia の知識は新しい Store 設計の選択肢として役立ちます。