文檔在這里: Vuex (vuejs.org)
1. Vuex 是什么?
Vuex 是一個專為 Vue.js 應用程序開發(fā)的狀態(tài)管理模式幌衣。它采用集中式存儲管理應用的所有組件的狀態(tài),并以相應的規(guī)則保證狀態(tài)以一種可預測的方式發(fā)生變化碗殷。
狀態(tài)管理模式
讓我們從一個簡單的 Vue 計數(shù)應用開始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
這個狀態(tài)自管理應用包含以下幾個部分:
- state,驅動應用的數(shù)據(jù)源速缨;
- view锌妻,以聲明方式將 state 映射到視圖;
- actions旬牲,響應在 view 上的用戶輸入導致的狀態(tài)變化仿粹。
以下是一個表示“單向數(shù)據(jù)流”理念的簡單示意:
然而當多個組件共享狀態(tài)(也就是數(shù)據(jù))的時候,數(shù)據(jù)變動的來源變得不明顯原茅,邏輯變得繁瑣不清晰吭历。那么 vuex 就是將組件的共享狀態(tài)抽取出來,以一個全局單例模式來管理的员咽,專門為 Vue.js 設計的狀態(tài)管理庫毒涧,它利用 Vue.js 的細粒度數(shù)據(jù)響應機制來進行高效的狀態(tài)更新。
通俗地來講贝室,就是當頁面應用過多且復雜契讲,這個時候就適合用 vuex 在組件外部管理狀態(tài)。
2. 安裝
直接下載
npm install vuex --save
// npm 或 yarn
yarn add vuex
// 在一個模塊化的打包系統(tǒng)中滑频,必須顯式地通過 Vue.use() 來安裝 Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
CDN 引用
當使用全局 script 標簽引用 Vuex 時捡偏,不需要以上安裝過程。
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
3. State
創(chuàng)建一個 store峡迷,創(chuàng)建過程直截了當——僅需要提供一個初始 state 對象和一些 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)對象,以及通過 store.commit 方法觸發(fā)狀態(tài)變更:
store.commit('increment')
console.log(store.state.count) // -> 1
mapState函數(shù)
mapState 函數(shù)返回的是一個對象绘搞。它可以將多個對象合并為一個彤避,以使我們可以將最終對象傳給 computed 屬性。
其中
...
叫做 對象擴展運算符夯辖,可以將 mapState 返回的對象展開在所在位置上琉预。
4. Getters
Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣蒿褂,getter 的返回值會根據(jù)它的依賴被緩存起來圆米,且只有當它的依賴值發(fā)生了改變才會被重新計算。
Getter 接受 state 作為其第一個參數(shù)啄栓,如果需要的話也可以接受其他 getter 作為第二個參數(shù)娄帖。
實例中的
fullName()
函數(shù)可以換成 ...Vuex.mapGetters(['fullName'])
5. Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數(shù) (handler)昙楚。這個回調函數(shù)就是我們實際進行狀態(tài)更改的地方近速。
在上面的兩個例子之中已經使用到了 mutation 。
還可以向 mutation 傳入額外的參數(shù),當然也可以傳遞一個對象数焊。
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
Mutation 需遵守 Vue 的響應規(guī)則
- 需要提前在 store 中初始化好所有所需屬性永淌。
- 當需要在對象上添加新屬性時,你應該
- 使用 Vue.set(obj, 'newProp', 123), 或者
- 以新對象替換老對象佩耳。
Mutation 必須是同步函數(shù)
每一條 mutation 都會被記錄遂蛀,如果使用異步,在 devtools 上將會很難捕捉前一狀態(tài)與后一狀態(tài)的快照干厚。(實質上任何在回調函數(shù)中進行的狀態(tài)的改變都是不可追蹤的)
在組件中提交 Mutation
可以在組件中使用 this.$store.commit('xxx')
提交 mutation李滴,或者使用 mapMutations 輔助函數(shù)將組件中的 methods
映射為 store.commit
調用(需要在根節(jié)點注入 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 可以包含任意異步操作挂捅。
讓我們來注冊一個簡單的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象芹助,因此你可以調用 context.commit
提交一個 mutation,或者通過 context.state
和 context.getters
來獲取 state 和 getters闲先。
使用es6可以簡化代碼為:
actions: {
increment ({ commit }) {
commit('increment')
}
}
分發(fā)Action
Action 通過 store.dispatch
方法觸發(fā):
store.dispatch('increment')
我們可以在 action 內部執(zhí)行異步操作状土,也支持同樣的載荷方式和對象方式進行分發(fā):
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
amount: 10
})
// 以對象形式分發(fā)
store.dispatch({
type: 'incrementAsync',
amount: 10
})
來看一個更加實際的購物車示例,涉及到調用異步 API 和分發(fā)多重 mutation:
actions: {
checkout ({ commit, state }, products) {
// 把當前購物車的物品備份起來
const savedCartItems = [...state.cart.added]
// 發(fā)出結賬請求伺糠,然后樂觀地清空購物車
commit(types.CHECKOUT_REQUEST)
// 購物 API 接受一個成功回調和一個失敗回調
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失敗操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
簡單地來說蒙谓,就相當于我讓朋友幫我去買書,買成功了就跟這個管理員結賬训桶,如果不成功累驮,就去找另一個來解決。那么這個 checkout
就相當于朋友舵揭。
7. Modules
Vuex 允許我們將 store 分割成模塊(module)谤专。每個模塊擁有自己的 state、mutation午绳、action毒租、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割:
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)