Vuex

Vuex
LiSirVuex 是什么?
Vuex 是一个专为 Vue2.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。是一个状态共享框架
- Vuex是采用集中式管理组件依赖的共享数据的一个工具,可以解决不同组件数据共享的问题
Vuex基础-初始化功能(store)
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的**状态 (state)**。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
建立一个新的脚手架项目, 在项目中应用vuex
1 | $ vue create learnvuex |
初始化:
第一步:
npm i vuex --save=> 安装到**运行时依赖** => 项目上线之后依然使用的依赖 ,开发时依赖 => 开发调试时使用 (注意对应版本,vue2对应vuex3,vue3对应vuex4)第二步: 在main.js中
import Vuex from 'vuex'第三步:在main.js中
Vue.use(Vuex)=> 调用了 vuex中的 一个install方法第四步:
const store = new Vuex.Store({...配置项})第五步:在根实例配置 store 选项指向 store 实例对象
1 | //这是main.js中(第一步是安装vuex) |
Vuex基础—State
单一状态树
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT (opens new window))”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
组件中获得state
在组件中我们最简单的获取方式是通过计算属性来获得存储在state中的数据。
第一步:获取store实例,Vue2中通过this.$store来获取,Vue3中使用函数useStore这两种方法是等效的,只是不同版本获取方式不同
下面我用Vue3举例
1 | //引入函数 |
第二步:使用计算属性来动态的获取state中的数据,这样当state中数据更新时,会同步更新(注意vue2和vue3的计算属性写法不一样)
1 | //使用计算属性来访问store中存储的数据 |
第三步:模板渲染,查看结果
mapState 辅助函数(难点)
Vue2中,我们通常会按照下面这种写法:
这里引入这个函数目的是:当我们在state中存储了较多的数据时,通过上述方式会频繁的使用计算属性来获取数据,这样就导致了我们代码量增多,于是引入mapState函数来帮我们快速生成
1 | //导入辅助函数 |
使用该函数会自动帮我们生成计算属性
这里的...是ES6中的延展运算符,它相当于会解析数组的内容并展开为多个独立的参数,比如
1 | let arr1 = [1, 2, 3]; |
1 | computed: { |
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
Vue3中,我们通常将其封装为一个hooks,来使用。封装的步骤如下:该hooks为userState
1 | //封装一个hooks来在vue3中使用mapState |
传入数组或对象
传入数组:当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
1 | <script setup> |
传入对象
1 | <script setup> |
在组件中使用
第一步:引入所封装的hooks
1 | //当数据较多时,我们应该使用mapState函数来帮助我们减少代码量 - vue3中使用自己封装的hooks来打成目的 |
第二步:模板渲染
1 | <div> |
注意点
- 不建议把所有属性均放在vuex的state中,维护有些状态严格属于单个组件,最好还是放在组件的局部存在。
Vuex基础—Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。这句话就是在说,在vue2中相当于computed,也就是计算属性。state在vue2中相当于data属性
getters是一个对象。而getters中的类计算属性将接收state作为第一个参数。当然,它也可以接受其他的getters作为其第二个参数
通过属性访问
由于Getter会暴露为$store.getters对象,所以你可以以属性的形式来访问这些值
1 | const store = createStore({ |
以属性进行访问
1 | //访问getters中的doneTodos - Vue2 |
在组件中使用
1 | //vue2 |
通过方法访问
原理:通过让getters返回一个函数,来实现传递参数
1 | getters:{ |
使用:
1 | const result = this.$store.getters.getTodoById(2) //查询 |
mapGetters辅助函数
该函数接收的参数是:store中getter项中的同名参数的数组或者对象
该函数的作用是把store中的getter映射到组件中的计算属性中去:
在Vue2中
1 | import { mapGetters } from 'vuex' |
如果你想将getter中的某个属性改名,可以使用对象形式
1 | ...mapGetters({ |
在Vue3中
1 | //封装一个hooks来在vue3中使用mapState |
Vuex基础—Mutation
在Vuex官方的定义中,如果想要更改仓库中(store)存储的数据(state中的数据)就是使用mutation,官方解释为在Vuex中mutation类似于事件。mutations中存放的是回调函数,该回调函数就是对state中数据操作的地方。它接收state作为第一个参数。同时它也向外提供了一个字符串的事件名称:**”increment”事件。**
1 | const store = createStore({ |
有了上述解释,我们就知道事件是不能直接调用的,而是注册某个事件,基本语法:
1 | store.commit("事件名") |
提交载荷(Payload)
所谓的”提交载荷”其实就是参数,我们知道mutations中的事件第一个参数是state,而第二个参数就是所谓的”提交载荷”。
官方说:在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
1 | mutations:{ |
组件中
1 | //写一个方法,注册store中的事件,点击让count加20 |
注册事件的另一种方式
在最开始我们知道,当我们想注册某个事件是要使用store.commit(),接下来介绍一种新的注册事件的方式,以对象的形式。
在vuex中,我们不更改mutations中的事件
1 | mutations:{ |
在组件中我们按照下面的这样的写法,可以同样达到效果。
1 | store.commit({ |
Mutation必须是同步函数
在mutations配置项中,事件(处理逻辑的函数)必须是一个同步函数,下面给出官方的例子:
1 | mutations: { |
现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
在组件中操作mutation
在Vue2中
你可以在组件中使用 this.$store.commit('xxx') 操作 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
1 | import { mapMutations } from 'vuex' |
Vuex基础—Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不像mutation那样直接去操作state中的数据。
- Action 可以包含任意异步操作。
让我们来注册一个简单的 action:
1 | const store = createStore({ |
actions中的函数接受一个与store实例具有相同的属性和方法的context对象,但context不是store本身。因此我们就可以调用context.conmmit()来操作mutation,还可以通过context.state或者context.getters来获取state和getter
操作Action
Action通过store.dispatch来触发
1 | store.dispatch('increment') |
Action中是支持进行异步操作的
1 | actions: { |
Action也支持载荷方式和对象方式进行操作
1 | //载荷方式触发 |
在组件中操作Action
在Vue2中
使用 this.$store.dispatch('xxx') 操作action,或使用mapActions辅助函数将组件的methods映射为store.dispatch调用,
mapAction辅助函数的本质是:在你的组件的 methods 对象中创建了一个名为 increment 的方法,这个方法内部调this.$store.dispatch('increment'),其他同理。
1 | import { mapActions } from 'vuex' |
Action中可以有多个异步操作
为什么可以有多个?
答:store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise
1 | actions: { |
整合多个异步操作,这里使用async/await简化promise操作
1 | // 假设 getData() 和 getOtherData() 返回的是 Promise |
Vuex基础—Module
由于单一状态树,整个应用的所有状态都会集中,导致数据臃肿,这时引入模块这个概念,它允许我们将store分割成Module,每个模块有自己的state,mutations,getters,actions
1 | const moduleA = { |
访问方式只是多加了一层,但是却让数据更好的维护。
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的state,而不是全局的state。
模块内的actions,局部的数据通过
context.state访问,而全局state需要使用context.rootState对于模块内的getters,根节点的state会作为第三个参数暴露。
如需知晓更多关于Module的内容请访问:https://vuex.vuejs.org/zh/guide/modules.html


