1、 項目結構
Vuex 並不限制你的代碼結構。但是,它規定了一些需要遵守的規則:
應用層級的狀態應該集中到單個 store 對象中。
提交 mutation 是更改狀態的唯一方法,並且這個過程是同步的。
異步邏輯都應該封裝到 action 裏面。
只要你遵守以上規則,如何組織代碼隨你便。如果你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。
對於大型應用,我們會希望把 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 # 產品模塊
2、插件
Vuex 的 store 接受 plugins 選項,這個選項暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數,它接收 store 作爲唯一參數:
const myPlugin = store => {
// 當 store 初始化後調用
store.subscribe((mutation, state) => {
// 每次 mutation 之後調用
// mutation 的格式爲 { type, payload }
})
}
然後像這樣使用:
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
2.1 在插件內提交 Mutation
在插件中不允許直接修改狀態——類似於組件,只能通過提交 mutation 來觸發變化。
通過提交 mutation,插件可以用來同步數據源到 store。例如,同步 websocket 數據源到 store(下面是個大概例子,實際上 createPlugin 方法可以有更多選項來完成複雜任務):
export default function createWebSocketPlugin (socket) {
return store => {
socket.on('data', data => {
store.commit('receiveData', data)
})
store.subscribe(mutation => {
if (mutation.type === 'UPDATE_DATA') {
socket.emit('update', mutation.payload)
}
})
}
}
const plugin = createWebSocketPlugin(socket)
const store = new Vuex.Store({
state,
mutations,
plugins: [plugin]
})
2.2 生成 State 快照
有時候插件需要獲得狀態的“快照”,比較改變的前後狀態。想要實現這項功能,你需要對狀態對象進行深拷貝:
const myPluginWithSnapshot = store => {
let prevState = _.cloneDeep(store.state)
store.subscribe((mutation, state) => {
let nextState = _.cloneDeep(state)
// 比較 prevState 和 nextState...
// 保存狀態,用於下一次 mutation
prevState = nextState
})
}
生成狀態快照的插件應該只在開發階段使用,使用 webpack 或 Browserify,讓構建工具幫我們處理:
const store = new Vuex.Store({
// ...
plugins: process.env.NODE_ENV !== 'production'
? [myPluginWithSnapshot]
: []
})