Vuex背后的基本思想
通过定义和隔离状态管理中的各种概念,并通过强制规则维持视图和状态间的独立性,让代码变得更结构化且易维护。
Vuex与全局对象的不同点
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
State 单一状态树
Vuex 使用单一状态树:用一个对象就包含了全部的应用层级状态。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
单状态树和模块化并不冲突
为何Mutations中只能放同步任务
看这个例子,api表示发请求
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
- 首先,devtools 能记录每一条 mutation 的基础是:捕捉到前一状态和后一状态的快照
- 现在,我们要在异步函数的回调中修改state中的数据。
- 然而,devtools 无法记录这一次的mutation,因为当 mutation 触发的时候,异步函数回调还没有被调用。
- 实质上,任何在回调函数中进行的状态的改变都是不可追踪的。
Action的context对象
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此有以下操作:
- 使用
context.commit调用 mutations 中的函数 - 使用
context.state访问state中的数据 - 使用
context.getters访问getters中的数据
然而,context 对象并不是 store 实例本身
如何组合多个Action
Action 通常是异步的,那么如何知道 action 什么时候结束呢?
更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?
首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
现在你可以:
store.dispatch('actionA').then(() => {
// ...
})
在另外一个 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,利用async/await组合action。
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
getters具有缓存
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)
就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
三个必须遵守的规则
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。