文檔在這里: Vuex (vuejs.org)
1. Vuex 是什么?
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
狀態(tài)管理模式
讓我們從一個(gè)簡(jiǎn)單的 Vue 計(jì)數(shù)應(yīng)用開始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
這個(gè)狀態(tài)自管理應(yīng)用包含以下幾個(gè)部分:
- state,驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源;
- view,以聲明方式將 state 映射到視圖;
- actions,響應(yīng)在 view 上的用戶輸入導(dǎo)致的狀態(tài)變化。
以下是一個(gè)表示“單向數(shù)據(jù)流”理念的簡(jiǎn)單示意:

然而當(dāng)多個(gè)組件共享狀態(tài)(也就是數(shù)據(jù))的時(shí)候,數(shù)據(jù)變動(dòng)的來源變得不明顯,邏輯變得繁瑣不清晰。那么 vuex 就是將組件的共享狀態(tài)抽取出來,以一個(gè)全局單例模式來管理的,專門為 Vue.js 設(shè)計(jì)的狀態(tài)管理庫(kù),它利用 Vue.js 的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來進(jìn)行高效的狀態(tài)更新。

通俗地來講,就是當(dāng)頁(yè)面應(yīng)用過多且復(fù)雜,這個(gè)時(shí)候就適合用 vuex 在組件外部管理狀態(tài)。
2. 安裝
直接下載
npm install vuex --save
// npm 或 yarn
yarn add vuex
// 在一個(gè)模塊化的打包系統(tǒng)中,必須顯式地通過 Vue.use() 來安裝 Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
CDN 引用
當(dāng)使用全局 script 標(biāo)簽引用 Vuex 時(shí),不需要以上安裝過程。
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
3. State
創(chuàng)建一個(gè) store,創(chuàng)建過程直截了當(dāng)——僅需要提供一個(gè)初始 state 對(duì)象和一些 mutation:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
現(xiàn)在,可以通過 store.state 來獲取狀態(tài)對(duì)象,以及通過 store.commit 方法觸發(fā)狀態(tài)變更:
store.commit('increment')
console.log(store.state.count) // -> 1
mapState函數(shù)
mapState 函數(shù)返回的是一個(gè)對(duì)象。它可以將多個(gè)對(duì)象合并為一個(gè),以使我們可以將最終對(duì)象傳給 computed 屬性。

其中
... 叫做 對(duì)象擴(kuò)展運(yùn)算符,可以將 mapState 返回的對(duì)象展開在所在位置上。
4. Getters
Vuex 允許我們?cè)?store 中定義“getter”(可以認(rèn)為是 store 的計(jì)算屬性)。就像計(jì)算屬性一樣,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。
Getter 接受 state 作為其第一個(gè)參數(shù),如果需要的話也可以接受其他 getter 作為第二個(gè)參數(shù)。

實(shí)例中的
fullName() 函數(shù)可以換成 ...Vuex.mapGetters(['fullName'])
5. Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方。
在上面的兩個(gè)例子之中已經(jīng)使用到了 mutation 。
還可以向 mutation 傳入額外的參數(shù),當(dāng)然也可以傳遞一個(gè)對(duì)象。
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
Mutation 需遵守 Vue 的響應(yīng)規(guī)則
- 需要提前在 store 中初始化好所有所需屬性。
- 當(dāng)需要在對(duì)象上添加新屬性時(shí),你應(yīng)該
- 使用 Vue.set(obj, 'newProp', 123), 或者
- 以新對(duì)象替換老對(duì)象。
Mutation 必須是同步函數(shù)
每一條 mutation 都會(huì)被記錄,如果使用異步,在 devtools 上將會(huì)很難捕捉前一狀態(tài)與后一狀態(tài)的快照。(實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的)
在組件中提交 Mutation
可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用(需要在根節(jié)點(diǎn)注入 store)。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`
// `mapMutations` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`
})
}
}
6. Action
Action 類似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接變更狀態(tài)。
- Action 可以包含任意異步操作。
讓我們來注冊(cè)一個(gè)簡(jiǎn)單的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函數(shù)接受一個(gè)與 store 實(shí)例具有相同方法和屬性的 context 對(duì)象,因此你可以調(diào)用 context.commit 提交一個(gè) mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
使用es6可以簡(jiǎn)化代碼為:
actions: {
increment ({ commit }) {
commit('increment')
}
}
分發(fā)Action
Action 通過 store.dispatch 方法觸發(fā):
store.dispatch('increment')
我們可以在 action 內(nèi)部執(zhí)行異步操作,也支持同樣的載荷方式和對(duì)象方式進(jìn)行分發(fā):
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
amount: 10
})
// 以對(duì)象形式分發(fā)
store.dispatch({
type: 'incrementAsync',
amount: 10
})
來看一個(gè)更加實(shí)際的購(gòu)物車示例,涉及到調(diào)用異步 API 和分發(fā)多重 mutation:
actions: {
checkout ({ commit, state }, products) {
// 把當(dāng)前購(gòu)物車的物品備份起來
const savedCartItems = [...state.cart.added]
// 發(fā)出結(jié)賬請(qǐng)求,然后樂觀地清空購(gòu)物車
commit(types.CHECKOUT_REQUEST)
// 購(gòu)物 API 接受一個(gè)成功回調(diào)和一個(gè)失敗回調(diào)
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失敗操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
簡(jiǎn)單地來說,就相當(dāng)于我讓朋友幫我去買書,買成功了就跟這個(gè)管理員結(jié)賬,如果不成功,就去找另一個(gè)來解決。那么這個(gè) checkout 就相當(dāng)于朋友。
7. Modules
Vuex 允許我們將 store 分割成模塊(module)。每個(gè)模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:
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 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)