Introduction
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.store(/* ... */)
const app = new Vue({
store
})
State
Single Source State
// Vuex Setting
import Vuex from 'vuex'
const store = new Vuex.store({
state: { count: 0 }
})
// Component
const componentOption = {
computed: {
count() { return this.$state.count }
}
}
使用 mapState(...) API 簡化綁定過程
import { mapState } from 'vuex'
const componentOption = {
computed: {
...mapState(['count']),
...mapState({
aliasCount: 'count',
functCount: state => state.count
})
}
}
Getter
形同 vue component option 中的 computed
// Vuex Setting
import Vuex from 'vuex'
const store = new Vuex.store({
state: { count: 0 },
getters: {
text(state, getters) { return `Count: ${state.count}` },
decoText(state, getters) { return `Deco ${getters.text}` }
}
})
// Component
const componentOption = {
computed: {
text() { return this.$store.state.getters.text }
}
}
Method-Style Access
使用 function 的方式調用 getter,且可以傳遞參數:
// Vuex Setting
import Vuex from 'vuex'
const store = new Vuex.store({
state: { count: 0 },
getters: {
text(state, getters) {
return (prefix) => `${prefix} Count: ${state.count}`
}
}
})
// Component 中調用
store.getters.text('Hola')
使用 mapGetters 簡化綁定過程
用法同 mapState:
import { mapState } from 'vuex'
const componentOption = {
computed: {
...mapGetters(['text'])
}
}
Mutation
- 在 Mutation 內部修改 state。
- mutation 內部的實作必須為 sync 的。
- mutation 的名稱建議設置為常數以便存取。
// Vuex Setting
import Vuex from 'vuex'
const INCREMENT = 'increment'
const VALUE = 'value'
const store = new Vuex.store({
state: { count: 0, value: "" },
mutation: {
[INCREMENT](state) { state.count++ },
// 可以傳遞參數 val
[VALUE](state, payload) { state.value = payload.value }
}
})
// Component 中調用
this.$store.commit(INCREMENT)
this.$store.commit(VALUE, { value: 'Hola' })
this.$store.commit({ type: VALUE, value: 'Hola' })
使用 mapMutations 綁定
// Component
import { mapMutations } from 'vuex'
const componentOption = {
methods: {
...mapMutations([add]),
callAdd() {
// Mapping 後的呼叫方式
this.add();
}
}
}
const componentOption = {
data() {
return { input: String }
},
methods: {
...mapMutations({
add: INCREMENT,
input: VALUE
})
}
}
// Component 中調用
this.add()
this.input(input)
Action
- 內部實作可為 async 的 mutation。
- 內部實作調用 mutation。
- 元件中呼叫
store.dispatch調用 action。 - 可以在 action 中透過
dispatch,呼叫其他 action。 store.dispatch(/* ... */)回傳Promise,可以依此來進行 async 行為設計(例如相依的修改、有優先順序的 ajax)。
// Vuex Setting
const INCREMENT = 'increment'
const store = new Vuex.store({
state: { count: 0 },
mutations: {
[INCREMENT](state) { state.count++ }
},
actions: {
[INCREMENT]({ commit, dispatch, state }, payload) { setTimeout(() => commit('increment'), 1000) }
}
})
// Component 中調用
this.$store.dispatch(INCREMENT)
使用 mapActions 綁定
用法同 mapState
// Component
import { mapActions } from 'vuex'
const componentOption = {
methods: {
...mapActions([INCREMENT])
}
}
Modules
讓你可以依據設計將 store 的設計拆分。
const moduleA = {
state: { /* ... */ },
mutations: { /* ... */ },
actions: { /* ... */ },
getters: { /* ... */ }
}
const moduleB = {
state: { /* ... */ },
mutations: { /* ... */ },
actions: { /* ... */ }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> `moduleA`'s state
store.state.b // -> `moduleB`'s state
module 內部實作時各 API 得到的第一個參數皆指向 module 內部的 scope。
const moduleA = {
state: { count: 0 },
mutations: {
// state 為 moduleA scope
increment(state) {
state.count++
}
},
getters: {
/*
* state, getter 為 moduleA scope
* rootState, rootGetter 為 root scope
*/
doubleCount(state, getters, rootState, rootGetter) {
return state.count * 2
}
},
actions: {
/**
* state, getter 為 moduleA 內部
* rootState, rootGetter 為 root scope
*/
incrementIfOddOnRootSum ({ state, getter, commit, rootState, rootGetter }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
Namespacce
預設情況下,不同 modules 的 getters、mutations、actions 都會被註冊在 global namesapce:
const moduleA = {
getters: {
holaA() { return 'Hola in ModuleA' }
}
}
const moduleB = {
getters: {
holaB() { return 'Hola in ModuleB' }
}
}
const store = new Vuex.store({
modules: {
a: moduleA,
b: moduleB
}
})
store.getters.holaA;
store.getters.holaB;
如果要將某 module 自 global 中拆開,則在該 module 中設定 namespace: true:
const moduleA = {
namespaced: true,
getters: {
holaA() { return 'Hola in ModuleA' }
}
}
const moduleB = {
getters: {
holaB() { return 'Hola in ModuleB' }
}
}
const store = new Vuex.store({
modules: {
a: moduleA,
b: moduleB
}
})
store.getters['a/holaA'];
store.getters.holaB;
在 Namespace 中呼叫 root 中的 mutations, actions
在 module 中如果要使用 root 上的 mutations, actions 時, 在 commit(/* ... */) 第三參數傳入 { root: true}
import Vuex from 'vuex'
const moduleA = {
namesapced: true,
state: { value: 'A' }
mutations: {
['UPDATE'](state, payload) { state.value = payload.value }
},
actions: {
['UPDATE_ROOT_VALUE'](context, payload) {
// 呼叫 root 中的 'UPDATE' commit, 本例中應為 moduleB.mutations['UPDATE']
context.commit('UPDATE', payload, { root: true })
}
}
}
const moduleB = {
state: { value: 'B' },
mutations: {
['UPDATE'](state, payload) { state.value = payload.value }
}
}
const store = new Vuex.Store({
modules: {
moduleA,
moduleB
}
})
store.dispatch('moduleA/UPDATE_ROOT_VALUE', { value: 'hello world!!' })
console.log(store.state.moduleB.value) // hello world!!
在 Namespace Module 中註冊全局的 action
import Vuex from 'vuex'
const moduleA = {
namesapced: true,
state: { value: 'A' }
mutations: {
['UPDATE'](state, payload) { state.value = payload.value }
},
actions: {
['UPDATE_FROM_GLOBAL_SCOPE'] {
root: true,
handler(context, payload) {
context.commit('UPDATE', payload)
}
}
}
}
const store = new Vuex.Store({
modules: { moduleA }
})
store.dispatch('UPDATE_FROM_GLOBAL_SCOPE', { value: 'hello world!!' });
console.log(store.state.moduleA.value) // hello world!!
使用 mapState, mapMutations, mapActions 快速註冊 Namespace Module
interface mapState<S> {
(
modulePath: string, // 例如: 'moduleA'
bindedState: Array<keyof S> | Object<string, (keyof S | (state: S) => any)>
): Object<string, any>
}
interface mapMutations<S> {
(
modulePath: string,
bindedState: Array<keyof S> | Object<string, (keyof S | (commit, ...args: any[]) => void)>
): Object<string, Function>
}
interface mapActions<S> {
(
modulePath: string,
bindedState: Array<keyof S> | Object<string, (keyof S | (dispatch, ...args: any[]) => void)>
): Object<string, Function>
}
Dynamic Register Module
import Vuex from 'vuex'
const store = new Vuex.Store({})
const moduleA = /* 某個 module */
store.registerModule('moduleA', moduleA)
const childModule = /* moduleA 的子 module */
store.registerModule(['moduleA', 'childModule'], childModule)
如果註冊 moduleA 之前已從 server-side render 拿到 moduleA 的 state 值, 可以在 dynamic registration 時予以保留:
store.registerModule('moduleA', moduleA, { preserveState: true });
State Pollution
如果重複註冊同個 module,如果 module 中的 state 宣告為 Object,則會造成 State Pollution,因此建議 state 宣告為 function:
const moduleA = {
state() { return { value: 'A' } }
}
Hot Reload
import Vuex from 'vuex'
const store = new Vuex.Store({ /* ... */ })
import('./newModules')
.then(module => {
store.hotUpdate({ /* ...同 Vuex.Store 的參數 */ })
});