Vuex 是什麼?
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。
狀態自管理應用包含以下幾個部分:
- state,驅動應用的數據源;
- view,以聲明方式將state映射到視圖;
- actions,響應在view上的用戶輸入導致的狀態變化。
Vuex 背後的基本思想,借鑑了 Flux、Redux、和 The
Elm Architecture。與其他模式不同的是,Vuex 是專門爲 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。
State
單一狀態樹
Vuex 使用 單一狀態樹 —— 是的,用一個對象就包含了全部的應用層級狀態。至此它便作爲一個『唯一數據源(SSOT)』而存在。這也意味着,每個應用將僅僅包含一個 store 實例。單一狀態樹讓我們能夠直接地定位任一特定的狀態片段,在調試的過程中也能輕易地取得整個當前應用狀態的快照。
在 Vue 組件中獲得 Vuex 狀態
那麼我們如何在 Vue 組件中展示狀態呢?由於 Vuex 的狀態存儲是響應式的,從 store 實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態:
// 創建一個 Counter 組件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
每當 store.state.count
變化的時候, 都會重新求取計算屬性,並且觸發更新相關聯的 DOM。
Getters
有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果有多個組件需要用到此屬性,我們要麼複製這個函數,或者抽取到一個共享函數然後在多處導入它 —— 無論哪種方式都不是很理想。
Vuex 允許我們在 store 中定義『getters』(可以認爲是 store 的計算屬性)。Getters 接受 state 作爲其第一個參數:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Getters 會暴露爲 store.getters
對象:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Mutations
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutations 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作爲第一個參數:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態
state.count++
}
}
})
你不能直接調用一個 mutation handler。這個選項更像是事件註冊:“當觸發一個類型爲 increment
的 mutation 時,調用此函數。”要喚醒一個 mutation handler,你需要以相應的 type 調用 store.commit 方法:
store.commit('increment')
Actions
Action 類似於 mutation,不同在於:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意異步操作。
分發 Action
Action 通過 store.dispatch
方法觸發:
store.dispatch('increment')
Modules
使用單一狀態樹,導致應用的所有狀態集中到一個很大的對象。但是,當應用變得很大時,store 對象會變得臃腫不堪。
爲了解決以上問題,Vuex 允許我們將 store 分割到模塊(module)。每個模塊擁有自己的 state、mutation、action、getters、甚至是嵌套子模塊——從上至下進行類似的分割:
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 的狀態
store.state.b // -> moduleB 的狀態
項目結構
Vuex 並不限制你的代碼結構。但是,它規定了一些需要遵守的規則:
-
應用層級的狀態應該集中到單個 store 對象中。
-
提交 mutation 是更改狀態的唯一方法,並且這個過程是同步的。
-
異步邏輯都應該封裝到 action 裏面。
只要你遵守以上規則,如何組織代碼隨你便。如果你的 store 文件太大,只需將 action、mutation、和 getters 分割到單獨的文件。
對於大型應用,我們會希望把 Vuex 相關代碼分割到模塊中。下面是項目結構示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我們組裝模塊並導出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
└── modules
├── cart.js # 購物車模塊
└── products.js # 產品模塊
請參考購物車示例
API 參考
- dispatch可以是view視圖中觸發,也可以是程序業務邏輯來觸發
- actions通過commit方法發出一個改變事件
- mutations中具體操作state的改變
- state的改變通過getter暴露給view,state改變後會立即通知用getter關聯起來的view。
- 創建一個Vuex.Store的實例,用於Vue實例。
Vuex引入一個store對象來實現數據更新的操作,不再是Vue實例自己來實現數據更新;
它可以讓(之前的)組件之間通訊來告知共享狀態的更新,變爲組件和store之間的通訊。
在使用mapState時,
// 爲了能夠使用 `this` 獲取局部狀態,
必須使用常規函數,如:
countPlusLocalState (state)
{
return state.count + this.localCount;
}
推薦一個博客,寫的Vuex教程不錯。