认识Vuex
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
- 它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
状态管理到底是什么?
- 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
- 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
- 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
- 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
- 当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
- 如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
- 不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。
简单来说 Vuex的作用就是 存放供各组件共享变量的地方。
管理什么状态呢?
但是,有什么状态时需要我们在多个组件间共享的呢?
- 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
- 比如用户的登录状态、用户名称、头像、地理位置信息等等。
- 比如商品的收藏、购物车中的物品等等。
- 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。
单界面状态管理的实现
这图片中的三种东西,怎么理解呢?
State:不用多说,就是我们的状态。(你姑且可以当做就是data中的属性)
View:视图层,可以针对State的变化,显示不同的信息。(这个好理解吧?)
Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变
counter需要某种方式被记录下来,也就是我们的State。
counter目前的值需要被显示在界面中,也就是我们的View部分。
界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions
多界面状态管理
Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
- 多个试图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
- 不同界面的Actions都想修改同一个状态(Home.vue需要修改,Profile.vue也需要修改这个状态)
- 也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
- 状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。
- 但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!
没错,Vuex就是为我们提供这个大管家的工具。
全局单例模式(大管家)
我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
之后,你们每个试图,按照我规定好的规定,进行访问和修改等操作。
这就是Vuex背后的基本思想。
案例:
组件间共享状态的方式一:传值
上面的传值案例是建立在存在父子关系的情况下的,如果没有这层关系,那该怎样共享状态呢。
vuex配置
配置:
import Vue from "vue";
import Vuex from 'vuex';
// 安装vuex插件
Vue.use(Vuex)
// 创建对象
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {}
})
// 导出
export default store;
挂载:
使用状态:
修改状态:官方不建议直接修改:
正确流程:
为什么直接能修改还要走这一系列流程?
因为只有这样,官方提供的Devtools 浏览器插件 才能监控到state被谁修改了,修改了什么,不然组件一多,一旦出问题,不好排查。Devtools可以跟踪记录state 的每一次修改记录。
你也可以 直接 绕过Actions 直接到Mutations 修改state
那么Actions存在的意义是什么?
官方推荐,如果有异步操作,需要放在Actions里操作,一旦Mutations里也存在异步 操作,那么Devtools 也是跟踪不到的。
那么 。什么时候有异步操作?
发送网络请求的时候。
这就是这几个角色的作用。
vuex基本使用:
Vuex核心概念
Vuex有几个比较核心的概念c
- Getters
- Mutation
- Action
- Module
我们对它进行一一介绍.
State
Vuex提出使用单一状态树, 什么是单一状态树呢?
- 英文名称是Single Source of Truth,也可以翻译成单一数据源。
但是,它是什么呢?我们来看一个生活中的例子。
OK,我用一个生活中的例子做一个简单的类比。
我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。
这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。
这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当然国家目前已经在完善我们的这个系统了)。
这个和我们在应用开发中比较类似:
如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。
所以Vuex也使用了单一状态树来管理应用层级的全部状态。
单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
Vue 官方推荐,你即使有很多的信息 也建议你只使用一个Store对象。不要搞一大堆的store
Getters
有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store中:
类似于Vue实例里面的计算属性 computed
基本使用:
Getters作为参数和传递参数
作为参数:
传递参数:
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
Mutation
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:
字符串的事件类型(type)(函数名字)
一个回调函数(handler),该回调函数的第一个参数就是state。
mutation的定义方式:
通过mutation更新
Mutation传递参数
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
参数被称为是mutation的载荷(Payload)
Mutation中的代码:
通过mutation更新:
Mutation提交风格
上面的通过commit进行提交是一种普通的方式
Vue还提供了另外一种风格, 它是一个包含type属性的对象
Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:
Mutation响应规则
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
-
1.提前在store种定义好的属性。
-
2 当给state中的对象添加新属性时, 应该使用下面的方式,使用其他的方式则没有响应式
方式一: 使用Vue.set(obj, ‘newProp’, 123)
方式二: 用新对象给旧对象重新赋值
Mutation常量类型
概念:
我们来考虑下面的问题:
在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
如何避免上述的问题呢?
在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.
我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.
具体怎么做呢?
我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.
Action
我们强调, 不要再Mutation中进行异步操作.
- 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求, 必然是异步的. 这个时候怎么处理呢?
- Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.
context是什么?
- context是和store对象具有相同方法和属性的对象.
也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.
定义action:
dispatch:
再由action commit
走的就是此图中的 123 步
Action返回的Promise
异步操作 配合Promise 使用
Module
Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等
我们按照什么样的方式来组织模块呢?
modules 中 state 实例:
使用:
modules 中 mutations :
modules 中 getters:
其实上面的用法 除了state 有一点小小的变化 ,其他都是一模一样的。使用时并不需要关系 是不是模块里面的,因为最终都是在store里
getters : 从模块中获取根的值
modules 中 Actions:
项目中的目录结构