为什么需要 Vuex

一种状态管理模式,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们把组件的共享状态抽取出来,以一个全局单例模式管理。

Vuex - 图1

什么时候需要 Vuex

当需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它

Mutation

更改 Vuex 的 store 中的状态的唯一方法

Mutation 需遵守 Vue 的响应规则

既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:

  1. 最好提前在你的 store 中初始化好所有所需属性。
  2. 当需要在对象上添加新属性时,你应该:
  • 使用 Vue.set(obj, 'newProp', 123), 或者
  • 以新对象替换老对象。例如,利用对象展开运算符 (opens new window)我们可以这样写:
    1. state.obj = { ...state.obj, newProp: 123 }

    Mutation 必须是同步函数

因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

Action

与 Mutation 的不同

Action 类似于 Mutation,不同在于:

  • Action 提交的是 Mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

    如何知道 action 什么时候结束

    Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise。一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

  1. const moduleA = {
  2. state: () => ({ ... }),
  3. mutations: { ... },
  4. actions: { ... },
  5. getters: { ... }
  6. }
  7. const moduleB = {
  8. state: () => ({ ... }),
  9. mutations: { ... },
  10. actions: { ... }
  11. }
  12. const store = new Vuex.Store({
  13. modules: {
  14. a: moduleA,
  15. b: moduleB
  16. }
  17. })
  18. store.state.a // -> moduleA 的状态
  19. store.state.b // -> moduleB 的状态

表单处理

双向绑定的计算属性

必须承认,这样做比简单地使用“v-model + 局部状态”要啰嗦得多,并且也损失了一些 v-model 中很有用的特性。另一个方法是使用带有 setter 的双向绑定计算属性:

  1. <input v-model="message">
  1. <script>
  2. // ...
  3. computed: {
  4. message: {
  5. get () {
  6. return this.$store.state.obj.message
  7. },
  8. set (value) {
  9. this.$store.commit('updateMessage', value)
  10. }
  11. }
  12. }
  13. </script>

常见错误

[vuex] unknown action type: xxx

方法一:把命名空间 代码注释掉

如果项目数据多,不建议 注释掉命名空间,不然方法名字重复了会造成混乱。如果有命名空间,只要文件名字不一样,方法名字一样也可以。

  1. export default {
  2. // namespaced: true,
  3. state,
  4. getters,
  5. actions,
  6. mutations
  7. }

方法二:this.$store.commit('app/changeNum') 或者使用 mapActions

  1. ...mapActions({
  2. add: "app/changeNumAsync",
  3. sub: "app/subtract"
  4. })

参考

【1】Vuex学习手记
【2】Vuex Next 官方文档
【3】Vuex 3.x 官方中文文档
【4】vuex — scrimba 交互视频