Vuex复习总结

112 阅读7分钟

1.什么是Vuex?

Vuex是一个专为vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex相当于共享项目中的数据,在应用中任意组件中都可以调用

简单概念:每个Vuex应用的核心就是store(仓库)。“store”基本上就是一个容器,它包含着应用中大部分的状态(state)Vuex单纯的全局对象有以下两点不同。

1.Vuex的状态存储是响应式的。当Vue组件从store仓库中读取状态的时候,若store仓库中状态发生变化,那么相应的调用该状态的组件也会相应的高效更新。

2.你不能直接改变store仓库中的状态。改变sotre中的状态的唯一途径就是显示地提交(commitmutation。这样促使我们可以方便地跟踪每个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

Store简单应用

安装Vuex之后,简单的创建一个store仓库和一个状态,一些mutation image.png

现在我们可以通过store.state来获取状态对象,以及通过store.commit方法触发状态变更: image.png

为了在Vue组件中访问this.$store property,你需要为Vue实例提供创建好的store,Vuex提供一个从根组件向所有子组件,以store选项方式“注入”该store的机制: image.png

现在我们可以从组件的方法提交一个变更: image.png

再次强调,我们通过提交mutation的方式,而非直接改变store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样我们在阅读代码的时候能够更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。(就是我们必须通过commit这种方式去改变状态,这让我们就能记录每次状态的改变,方便调试

由于store中的状态是响应式的,在组件中调用store中的状态,我们仅需要在计算属性中返回即可,触发变化也仅仅是在组件的methods中提交mutation即可。

2.State状态

Vuex使用的单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源”而存在。这也表明每个应用将仅仅包含一个store实例。单一状态树让我们能够直接地定位任意一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。存储在Vuex中的数据和Vue实例中的data遵循相同的规则,例如状态对象必须是纯粹(plain)的。

我们创建的store仓库 image.png

State状态在组件中展示的方式

Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是计算属性,通过计算属性来返回我们想要的状态。

第一种:单一组件导入store实例方式

通过import导入store实例, image.png

每当store.state.count变化的时候,都会重新求取计算属性,并且触发更新相关联的DOM。 然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用state的组件中需要频繁地导入,并且在测试组件时需要模拟状态。

第二种: 根组件注册将状态“注入”到子组件中

Vuex通过store选项,提供了一种机制将状态从根组件“注入”到每个子组件中(需调用Vue.use(Vuex)); image.png

通过在根实例中注册store仓库选项,该store实例会注入到根组件下的所有子组件中,且子组件能通过this.$store访问到。 image.png

第三种: mapState辅助函数

当一个组件需要获取多个状态的时候,我们为了减少代码的重复性,我们可以使用mapState辅助函数帮助我们生成计算属性值。

首先我们导入mapState辅助函数 image.png

mapState辅助函数与对象展开运算符的联动,mapState函数返回的是一个对象。我们如何将它与局部计算属性混合使用? 以前,我们需要使用一个工具函数将多个对象合并成一个,以使我们可以将最终对象传给computed计算属性,使用扩展运算符就极大的简化了写法: image.png

mapState在computed中三种使用方法:

①箭头函数取状态法

image.png

②常规函数取状态法

image.png

③映射去状态法:通过计算属性值的名称与state的子节点名称相同时,我们就可以通过给mapState传一个字符串数组,来获取状态。

image.png

3.Getter(相当于store的计算属性)

有时候我们需要根据store中的state状态衍生出新的状态,并且在多个子组件中都需要复用的情况,我们可以使用getter,因为它相当于store的计算属性。

getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 getter接受state作为其第一个参数: image.png image.png

通过属性访问

getter会暴露store.getters对象,我们可以通过属性的形式访问这些值: image.png

getter也可以接受其他getter作为其第二个参数: image.png image.png

通过getters参数我们可以访问getter对象中的属性值, 我们可以很容易地在任何组件中使用它:

image.png image.png image.png 注意,getter在通过属性访问时是作为Vue的响应式系统的一部分缓存其中的。

通过方法访问

我们可以通过让getter返回一个函数,来实现给getter传参。在我们对store里的数组进行查询时非常有用。

image.png image.png

注意,getter在通过方法访问时,每次都会去进行调用,而不会缓存结果。

mapGetters辅助函数

mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性: image.png 如果你想将一个getter属性另取一个名字,使用对象形式: image.png

4.Mutation(state状态变更的唯一途径)

更改Vuexstore中的状态的唯一方法是提交mutation。Vuex中的mutation类似于事件,每个mutation都有一个字符串事件类型type)和一个回调函数handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数image.png image.png

给按钮绑定事件:

image.png image.png

我们没有直接调用mutation handler,而是通过store.commit 出发一个类型‘increment’的mutation,这个increment就是我们上下文提到的字符串的事件类型(type),最终得以更改状态。

提交载荷(Payload)

在通过store.commit触发函数时,第一个参数传的是字符串的事件类型(type),那么我们向commit传入第二个参数时,这就是mutation的载荷(payload): image.png image.png image.png image.png

此时点击状态加十按钮时,状态每次变更增加10。大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读: image.png image.png

对象风格的提交方式

提交mutation的另一种方式是直接使用包含type属性的对象: image.png 当使用对象风格的提交方式,整个对象都作为载荷(payload)传给mutation函数,因此handler保持不变: image.png

Mutation需遵守Vue的相应规则

既然Vuex的store中的状态是响应式的,那么当我们变更状态时,监视状态的Vue组件也会自动更新。

这就意味着Vuex中的mutation也需要与使用Vue一样遵守一些注意事项:

  1. 最好提前在我们的store中初始化好我们所需要的属性。
  2. 当我们需要在对象上添加新属性时,我们应当使用以下两种方式新增对象属性, ①使用Vue.set(obj,'name','asd'),第一个参数obj是我们想要新增属性的对象,第二个参数'name'是我们想要新增的属性名key值,第三个参数是我们想要新增属性的值'value'; ②以新对象替换老对象。例如,利用对象展开运算符我们可以这样写

image.png image.png image.png

使用常量替代Mutation事件类型

使用常亮代替mutation事件类型在各种Flux实现中是很常见的模式。这样可以使linter之类的工具发挥作用,同时把这些常量放到一个文件里,可以使代码更规整,所有项目中包含的mutation都一目了然: image.png image.png

Mutation必须是同步函数

一条重要的原则就是要记住mutation必须是同步函数image.png

如果是这种异步的,就没法精确的记录状态了。

现在想象,我们正在debug一个app并且观察devtool中的mutation的日志。每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照。 然而,在上面的例子中mutation中的异步函数中的回调让这中情况不可能完成,因为当mutation触发的时候,回调函数还没有被调用,devtools不知道什么时候回调函数实际上被调用---实质上任何在回调函数中进行的状态的改变都是不可追踪的。

在组件中提交Mutation

我们可以在组件中使用this.$store.commit('xxx')提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为store.commit调试(需要在根节点注入store)。 image.png

重点:::在vuex中,mutation都是同步事务。

5.Action(异步处理,提交mutation)

action相当于处理异步事件的方法,action提交的是mutation,而不是直接变更状态,action可以包含任意异步操作image.png Action函数接受一个与store实例具有相同方法和属性的context对象,所有我们可以通过这个对象调用context.commit提交一个mutation,或者通过context.statecontext.getters来获取state和getters。

实践中,我们会经常用到ES2015的参数结构来简化代码。 image.png

分发Action,也就是调用action的方式:

Action通过store.dispatch方法触发: image.png

因为mutation必须是同步执行的限制,所以我们来用Action来处理异步操作image.png image.png

Actions支持同样的载荷方式对象方式进行分发: image.png image.png

官方文档的购物车案例,涉及到调度异步API和分发多重mutation: image.png 我们进行了一系列的异步操作,并且通过提交mutation来记录action产生的副作用(即状态变更)。

在组件中分发Action

我们在组件中使用this.$store.dispatch('')分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch调用(需要先在根节点注入store): image.png image.png image.png image.png

组合Action

Action通常是异步的,那么我们如何知道action什么时候结束,我们如何才能组合多个action,以处理更加复杂的异步流程?store.dispatch(),可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍旧返回Promise: image.png image.png image.png

最后,我们可以利用async/await,我们可以如下组合action: image.png

一个store.dispatch在不同模块中可以触发多个action函数。在这种情况下,只有当所有触发函数完成后,返回的Promise才会执行。