Vuex
What is Vuex
- State を予見可能な形で集中管理する
- 中規模・大規模なアプリに最適
- 小規模であればstore パターンで事足りるかも
はじめに
Vuex を使うのと、グローバルオブジェクトを使う場合との違い
- Vuex の store から取得した値はリアクティブになる
- Vuex の値は mutation を commit することによってのみ変更できる。これにより値の変更が追跡可能になる。
基本的な使い方
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
});
// mutationをコミットする
store.commit('increment');
// storeの値を取得する
console.log(store.state.count); // => 1
State
Single State Tree
Vuex の State は単一のオブジェクトで管理される。
コンポーネントで store を使う
// let's create a Counter component
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count() {
return store.state.count;
},
},
};
computed プロパティとすることで、store の更新を検知し、コンポーネントをアップデートできるようになる。
上記のやり方はスケールしないので、通常は下記のようにする。
// root Vue インスタンス
const app = new Vue({
// provide the store using the "store" option.
// this will inject the store instance to all child components.
store,
/* ... */
});
// コンポーネント
const Counter = {
computed: {
count() {
return this.$store.state.count;
},
},
};
mapState
react-redux の mapStateToProps と同じ機能。
this.$store.state
配下の各値をコンポーネントのcomputed
に手早くマップするときに使う。
import { mapState } from 'vuex';
export default {
computed: {
...mapState({
// ファンクションを使う方法
count: (state) => state.count,
// storeのキー名を使う方法
countAlias: 'count',
// ローカルStateと組み合わせる場合はノーマル関数を使う
countPlusLocalState(state) {
return state.count + this.localCount;
},
}),
},
// もしくは、キー名を配列で渡す方法もある
computed: {
...mapState([
// map this.count to store.state.count
'count',
]),
},
};
ローカルの computed と一緒に使う場合は下記のようにする。
computed: {
...mapState({})
localComputed () { /* ... */ },
}
Getters
- state の getter を設定することができる。computed の store 版と思えば OK。
- getter の再計算は、依存する値が変更された時のみ行われる(computed と一緒の挙動)
const store = new Vuex.Store({
state: {
count: 1,
},
getters: {
bigCount: (state) => state.count * 10,
// 第2引数に他のgetterを取ることもできる
moreBigCount: (state, getters) => getters.bigCount * 10,
},
});
Getter へのアクセス
コンポーネントからはthis.$store.getters
で取得できる。
computed: {
bigCount () {
return this.$store.getters.bigCount
}
}
応用編(メソッドスタイル)
getter がファンクションを返すようにすることで、コンポーネント側からクエリを投げることができる。
// store側
getters: {
getTodoById: (state) => (id) => {
return state.todos.find((todo) => todo.id === id);
};
}
// コンポーネント側
store.getters.getTodoById(2); // -> { id: 2, text: '...', done: false }
mapGetters
state と同じく、this.$store.getters
配下の各値を手早くコンポーネントのcomputed
にマップする時に使う。
import { mapGetters } from 'vuex'
export default {
computed: {
// 名前を変更せずにそのままマップする時
...mapGetters([
'doneTodosCount',
'anotherGetter',
])
// 名前を変更してマップした いとき
...mapGetters({
doneCount: 'doneTodosCount'
})
}
}
Mutations
各 mutation はtypeとhandlerを持つ。handler は第一引数にstate
をとる。
const store = new Vuex.Store({
mutations: {
// ファンクション名をstringにしたものがtypeである => 'increment'
// ファンクション自身が、handlerである
increment(state) {
// mutate state
state.count++;
},
},
});
mutation は直接実行することはできない。実行するときは下記のようにする。これがmutation
をcommit
するということ。
// 'increment' typeのmutationを実行してくださいよー
store.commit('increment');
payload
mutation を commit する時に、引数を渡すことができる。これがpayload
である。
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
Object-Style Commit
store.commit()
には、string ではなくオブジェクトを渡すこともできる。
この場合、このオブジェクトが全て payload として handler に渡される。
store.commit({
type: 'increment',
amount: 10,
});