[TOC]
一、 Vuex简介
1.1 官方解释
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
1.2 状态管理到底是什么?
状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
1.3 Vuex状态管理图例
二、Vuex的基本使用
2.1 Vuex五个核心概念总概
- state:Vuex的基本数据,用来存储变量
- getters:从基本数据(state)派生的数据,相当于state的计算属性
- mutation:提交更新数据的方法(里面是写方法的),必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。
- actions:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。
- modules:模块化Vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
注意: 一般在项目的src文件夹中新建store文件夹,Vuex相关代码都写到里面
案例:
2.2 state单一状态树
2.2.1 单一状态树解释:
- 用一个对象(主干)就包含了全部的(分支)应用层级状态
- 每个应用将仅仅包含一个 store 实例对象(主干)
单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
2.2.2 如何在 Vue 组件中展示状态呢?
从store实例中读取状态最简单的方法就是在计算属性中返回某个状态。
示例:
例如我们在state中定义一个count属性,给它赋一个值为10,store.js文件内容如下所示:
然后创建一个 Counter组件,在组件中返回count,内容如下所示:
每当 store.state.count 变化的时候,都会重新求取count属性,并刷新界面。
这种模式依赖于全局的管理员 store,如果模块多了,那么每个模块或者页面只要用到了这个 state 里面的数据,都得把 store 引入进来,这样的操作确实有点难受。所以出现了下面这种解决办法。
Vuex 通过store 选项,提供了一种机制将状态从根组件 “注入” 到每一个子组件中:
在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过this.$store 访问到,在组件中直接访问:this.$store.state.count
2.2.3 mapState辅助函数
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
示例:
在使用mapState函数之前,需要先引入它:import { mapState } from ‘Vuex’
引入后才可以开始使用,它两种用法,可以接受一个对象或接受一个数组。
对象用法如下:
数组用法如下所示:
2.3 getters
2.3.1 前言
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数:
2.3.2 通过属性访问
Getter会暴露为 store.getters 对象,你可以以属性的形式访问这些值:
store.getters.doneTodos // [{ id: 1, text: ‘…’, done: true }]
Getter 也可以接受其他 getter 作为第二个参数:
我们可以很容易地在任何组件中使用它:
注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。
2.3.3 通过方法访问
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
2.3.4 案例:获取学生年龄大于20的个数
注意:getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
2.4 mutations
2.4.1 Mutation状态更新
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:
- 字符串的事件类型(type)
- 一个回调函数(handler),该回调函数的第一个参数就是state
mutation的定义方式:
通过mutation更新:
2.4.2 Mutation传递参数
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
- 参数被称为是mutation的载荷(Payload)
Mutation中的代码:
但是如果参数不是一个呢?
- 比如我们有很多参数需要传递.
- 这个时候, 我们通常会以对象的形式传递, 也就是payload是一个对象.
- 这个时候可以再从对象中取出相关的信息.
案例:使+5按钮和+10按钮起效果,点击添加学生就给stu信息添加到数组中
2.4.3 Mutation另一种提交风格
上面的通过commit进行提交是一种普通的方式,Vue还提供了另外一种风格, 它是一个包含type属性的对象
Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:
2.4.4 Mutation响应规则
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
- state里面的内容属性都会被添加到响应式系统中,当系统监听到属性发生了变化,就会通知所有界面中用到该属性的地方进行更新,页面就会刷新
这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时, 使用下面的方式:
- 方式一: 使用Vue.set(obj, ‘newProp’, 123)
- 方式二: 用新对象给旧对象重新赋值
我们来看一个例子:
- 当我们点击更新信息时, 界面并没有发生对应改变.
如何才能让它改变呢?
- 查看下面代码的方式一和方式二
- 都可以让state中的属性是响应式的.
2.4.5 使用Mutation常量类型
我们来考虑下面的问题:
- 在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
- 当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
- 方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
如何避免上述的问题呢?
- 在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.
- 我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.
具体怎么做呢?
- 我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
- 定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.
2.4.6 Mutation同步函数
通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
- 主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
- 但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成.
比如我们之前的代码, 当执行更新时, devtools中会有如下信息: 图1
但是, 如果Vuex中的代码, 我们使用了异步函数(定时器): 图2
你会发现state中的info数据一直没有被改变, 因为他无法追踪到,所以通常情况下, 不要再mutation中进行异步的操作
2.5 actions
2.5.1 Action的基本定义
我们强调, 不要再Mutation中进行异步操作.
- 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求, 必然是异步的. 这个时候怎么处理呢?
- Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.
- Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用context.commit提交一个 mutation,或者通过context.state和context.getters来获取 state 和 getters。
让我们来注册一个简单的 action:
context是什么?
- context是和store对象具有相同方法和属性的对象.
- 也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.
- 但是注意, 这里它们并不是同一个对象, 为什么呢? 我们后面学习Modules的时候, 再具体说.
这样的代码是否多此一举呢?
- 我们定义了actions, 然后又在actions中去进行commit, 这不是脱裤放屁吗?
- 事实上并不是这样, 如果在Vuex中有异步操作, 那么我们就可以在actions中完成了.
2.5.2 Action的分发
- 在Vue组件中, 如果我们调用action中的方法, 那么就需要使用store.dispatch() 方法
- 同样的, 也是支持传递payload
2.5.3 Action返回的Promise
前面我们学习ES6语法的时候说过, Promise经常用于异步操作.
在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject.
2.6 modules
Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
- Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
- 当应用变得非常复杂时,store对象就有可能变得相当臃肿.
- 为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等
上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写?
- 我们在moduleA中添加state、mutations、getters
- mutation和getters接收的第一个参数是局部状态对象
补充:
modules里面action的写法: 局部状态通过 context.state 暴露出来,根节点状态则为context.rootState
modules里如果getters中也需要使用全局的状态, 可以接受更多的参数:
三、结语
这是本人在学习vuex时的笔记总结,对vuex进行了简单的概述,并附上了对应的案例,有什么问题希望大家指正!