缓存,更新
- data 属性对象中定义的属性值会被缓存,不会随依赖变量变化而计算更新;
- computed 属性对象中定义的属性值会被缓存,会随依赖变量变化而计算更新;
- filter 属性对象中定义的属性值不会被缓存,会随依赖变量变化而计算更新;
+ watch 属性对象中定义的属性值不会被缓存,
- 会随在属性名'a'所对应的实例属性this['a']之变化时,
- 执行watch中定义的'a'属性标示的函数;
+ Vuex 通过 store 选项,
- '提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex))';
+ 通过在根实例中注册 store 选项,
- 该 store实例会注入到根组件下的所有子组件中,
- 且子组件能通过 this.$store 访问到。;
- 当一个组件需要获取多个状态时候,
- 将这些状态都声明为计算属性会有些重复和冗余。
- 为了解决这个问题,
- '可以使用 mapState 辅助函数帮助我们生成计算属性';
- 当映射的计算属性的名称与 state 的子节点名称相同时,
- 我们也可以给 mapState 传一个字符串数组。
- 有时候我们需要从 store 中的 state 中派生出一些状态;
+ 如果有多个组件需要用到此属性,
- 我们要么复制这个函数,
- 或者抽取到一个共享函数然后在多处导入它,
- 无论哪种方式都不是很理想;
+ Vuex 允许我们在 store 中定义“getter”
- '可以认为getter是 store 的计算属性',
- 就像计算属性一样;
- getter的返回值会根据它的依赖被缓存起来,
- 而且且只有当它的依赖值发生了改变才会被重新计算;
- Getter 接受 state 作为其第一个参数;
- Getter 会暴露为 store.getters 对象;
- Getter 也可以接受其他 getter 作为第二个参数;
- 我们可以很容易地在任何组件中使用它;
+ 也可以通过让 getter 返回一个函数,
- 来实现给 getter 传参,
- 在对 store 里的数组进行查询时非常有用;
+ mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性;
- 如果想将一个 getter 属性另取一个名字,
- 使用对象形式;
+ 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation;
+ Vuex 中的 mutation 非常类似于事件:
- 每个 mutation 都有一个字符串的 事件类型 (type),
- 一个 回调函数 (handler);
+ 这个回调函数就是我们实际进行状态更改的地方,
- 并且它会接受 state 作为第一个参数;
+ 你不能直接调用一个 mutation handler。
+ 这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”;
- 要唤醒一个 mutation handler,
- 你需要以相应的 type 调用 store.commit 方法:
+ 提交载荷(Payload)
+ 你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):
- store.commit('increment', 10);
+ 在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
- store.commit('increment', { amount: 10 });
+ 对象风格的提交方式
+ 提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
- store.commit({ type: 'increment', amount: 10 });
- 当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变;
+ Mutation 需遵守 Vue 的响应规则
+ 既然 Vuex 的 store 中的状态是响应式的,
- 那么当我们变更状态时,
- 监视状态的 Vue 组件也会自动更新;
+ 这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用 Vue.set(obj, 'newProp', 123),
- 或者,以新对象替换老对象: state.obj = { ...state.obj, newProp: 123 };
+ 使用常量替代 Mutation 事件类型
+ 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。
- 这样可以使 linter 之类的工具发挥作用,
- 同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然;
- '用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。';
+ Mutation 必须是同步函数
- 当 mutation 触发的时候,
- 回调函数还没有被调用,
- devtools 不知道什么时候回调函数实际上被调用;
- '实质上任何在回调函数中进行的的状态的改变都是不可追踪的';
+ 在组件中提交 Mutation
- 可以在组件中使用 this.$store.commit('xxx') 提交 mutation,
- 或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用('需要在根节点注入 store')
+ Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态;
- Action 可以包含任意异步操作;
+ Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,
- 因此可以调用 context.commit 提交一个 mutation,
- 或者通过 context.state 和 context.getters 来获取 state 和 getters;
- '实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候);
+ 分发 Action
+ Action 通过 store.dispatch 方法触发:
- store.dispatch('increment');
- 在 action 内部执行异步操作;
+ Actions 支持同样的载荷方式和对象方式进行分发:
+ 以载荷形式分发:
- store.dispatch('incrementAsync', { amount: 10 });
+ 以对象形式分发:
- store.dispatch({ type: 'incrementAsync', amount: 10 });
+ 在组件中分发 Action
- 你在组件中使用 this.$store.dispatch('xxx') 分发 action,
- 或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store);
+ 组合 Action
- Action 通常是异步的,
+ 首先store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,
- 并且 store.dispatch 仍旧返回 Promise;
+ 而且我们利用 async / await 这个 JavaScript 即将到来的新特性来组合 action;
+ 一个 store.dispatch 在不同模块中可以触发多个 action 函数。
- 在这种情况下,只有当所有触发函数完成后,
- 返回的 Promise 才会执行。
+ 由于使用单一状态树,
- 应用的所有状态会集中到一个比较大的对象。
- 当应用变得非常复杂时,
- store 对象就有可能变得相当臃肿。
+ 为了解决以上问题,
- 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 的状态 */ store.state.b; /* -> moduleB 的状态 */
+ 模块的局部状态
- 对于模块内部的 mutation 和 getter,
- 接收的第一个参数是模块的局部状态对象。
+ 对于模块内部的 action,
- 局部状态通过 context.state 暴露出来,
- 根节点状态则为 context.rootState;
+ 对于模块内部的 getter,
- 根节点状态会作为第三个参数暴露出来;
+ 命名空间
+ 默认情况下,
- 模块内部的 action、mutation 和 getter 是注册在全局命名空间的,
- 这样使得多个模块能够对同一 mutation 或 action 作出响应。
+ 如果希望你的模块具有更高的封装度和复用性,
- 你可以通过添加 namespaced: true 的方式使其成为命名空间模块。
- 当模块被注册后,
- 它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
+ 启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit;
- 换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀;
- 更改 namespaced 属性后不需要修改模块内的代码;
+ 在命名空间模块内访问全局内容(Global Assets)
- 如果你希望使用全局 state 和 getter,rootState 和 rootGetter 会作为第三和第四参数传入 getter,
- 也会通过 context 对象的属性传入 action。
+ 若需要在全局命名空间内分发 action 或提交 mutation,
- 将 { root: true } 作为第三参数传给 dispatch 或 commit 即可;
+ 带命名空间的绑定函数
- 当使用 mapState, mapGetters, mapActions 和 mapMutations 这些函数来绑定命名空间模块时,
- 写起来可能比较繁琐;
+ 对于这种情况,
- 你可以将模块的空间名称字符串作为第一个参数传递给上述函数,
- 这样所有绑定都会自动将该模块作为上下文;
+ 你可以通过使用 createNamespacedHelpers创建基于某个命名空间辅助函数;
- 它返回一个对象,
- 对象里有新的绑定在给定命名空间值上的组件绑定辅助函数;
+ 给插件开发者的注意事项
- 如果你开发的插件(Plugin)提供了模块并允许用户将其添加到 Vuex store,
- 可能需要考虑模块的空间名称问题;
- 对于这种情况,你可以通过插件的参数对象来允许用户指定空间名称;
+ 模块动态注册
- 在 store 创建之后,
+ 你可以使用 store.registerModule 方法注册模块:
- store.registerModule('myModule', { /* ... */ })
- store.registerModule(['nested', 'myModule'], { /* ... */ })
- 之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态;
+ 模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态;
- 例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,
- 实现应用的路由状态管理;
+ 也可以使用 store.unregisterModule(moduleName) 来动态卸载模块;
- 注意,不能使用此方法卸载静态模块(即创建 store 时声明的模块);
+ 在注册一个新 module 时,
- 你很有可能想保留过去的 state,
- 例如从一个服务端渲染的应用保留 state;
+ 你可以通过 preserveState 选项将其归档:
- store.registerModule('a', module, { preserveState: true })。
+ 模块重用
+ 有时我们可能需要创建一个模块的多个实例,例如:
- 创建多个 store,
+ 他们公用同一个模块
- 例如当 runInNewContext 选项是 false 或 'once' 时,
- 为了在服务端渲染中避免有状态的单例;
+ 在一个 store 中多次注册同一个模块;
+ 如果我们使用一个纯对象来声明模块的状态,
- 那么这个状态对象会通过引用被共享,
- 导致状态对象被修改时 store 或模块间数据互相污染的问题;
+ 实际上这和 Vue 组件内的 data 是同样的问题;
- 因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持);