学习vuex状态管理模式

前言

我们知道,在 Vue 中父子组件传值是最常用的知识点之一,项目中功能组件和服务组件的封装都会有各种各样的数据传递,用 props 定义字段或者是子组件 emit 来通信。但是,当我们的项目的复杂度逐渐增长的时候,组件会越来越多,而且一些组件并不存在调用关系,一些数据需要共享的时候,那么问题就来了:

  1. 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力;
  2. 采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝,代码冗余,慢慢的难以维护。

所以我们在项目中经常是全局定义一个实例“全局单例模式”,当然你也可以定义多个实例来共享一个数据源,我们来看看官方图好了,来的比较直接一些:

enter image description here

我们可以看到,下方的两个组件,如果不使用 Vuex,或许就需要我们定义两个组件来维护两份同一个接口返回的数据;但是两个组件是各取所需使用返回的个别字段,不仅增加了请求,还不能同步。如果用 Vuex 来打理这份数据,共享同一个 store 数据池,当 store 中的数据改变的时候,两个组件中的数据状态都会第一时间同步,所有 store 中 state 的改变,都放置在 store 自身的 action 中去管理。

这种集中式状态管理能够更容易地理解哪种类型的 Mutation 将会发生,以及它们是如何被触发。就是说,action 做了一个触发 Mutation 的动作,分发 dispatch 事件去通知 store 要改变数据了!!!总之,Vuex 大家都理解为一种各种状态的集中管理中心就好了。

准备

在这里大家需要安装好 Vuex 。

Vue 中我们推行极简的单向数据流,但是就像前面我们说的,当我们多个视图组件依赖于同一状态、来自不同视图组件的行为需要变更同一状态的时候,就会很头疼,经常组件之间来回复制粘贴,如果组件树能有一个被统一管理的数据池来获取数据和触发行为,何乐而不为呢?

需要注意的地方:

  1. 一个 Vuex 核心是 store,相当于一个池塘,而你的很多 state 可以当作,store 包含着 state。
  2. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  3. 我们不能直接改变 store 中的state状态。我们应执行 action 来分发(dispatch)事件通知 store 去改变,从而显式地提交 (commit)记录变更(mutation)
  4. Vuex 依赖 ES6 的 promise,大家注意浏览器的兼容

挂载实例、文件准备

我们在 vue-cli 脚手架搭建的项目中,因为是全局单例模式,所以我们先创建一个 store 文件夹,用来放我们的相关文件,我是按照模块来划分数据池,大家可以根据自己的需要,写到一起也是可以的,来看看文件目录结构,这就是刚刚新建的目录,我们在模块中有一个狗狗的模块,来集中管理全局关于狗狗的数据:

enter image description here

我们接着定义 index.js,其实很简单,就是引入 Vuex,这里的 vue.use(Vuex),将状态从根组件“注入”到每一个子组件中,然后 new 一个 store 推出去:

enter image description here

然后我们需要在 main.js 中引用一下,像 router 一样。

enter image description here

我们来看看浏览器中的 store 现在是什么状态,随便添加一个测试文本,因为我们的狗狗模块暂时是空的。

enter image description here

可以看到我们已经成功的有一个 store 了!在组件内,可以使用 this.$store.state.testText 访问到,或许计算属性是一个不错的选择呢~

恭喜你!完成了第一步,我们已经定义好了我们的数据池,还挂载到了我们全局实例上面,另外定义了一个狗狗模块,引入到了 store,接下来就让我们开始 dog 模块的编写吧!

dog 狗狗模块

我们可以看到下图中,我们定义了 type 类型,还有一个默认的 state 对象,里面是名字、年纪、好朋友。

enter image description here

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 Mutation。Vuex 中的 Mutation 非常类似于事件:每个 Mutation 都有一个字符串的 事件类型(type)和 一个 回调函数(handler)

这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。就像是事件注册:“当触发一个类型为 increment 的 Mutation 时,调用此函数”。

要唤醒一个 Mutation 的回调,我们需要以相应的 type 调用 store.commit 方法。

比如我们改变一下狗狗的名字,可以看到,我们用类型 DOG 定义了一个事件,参数也是一目了然,我们可以看到第一个参数是 state,第二个参数是新的名字,在 Mutations 中,我们叫做载荷(Payload)

enter image description here

通常情况下,Payload 是一个对象,这样属性可读性会比单独的好很多,所以我们修改一下:

enter image description here

敲黑板!敲黑板!

  1. Mutation 函数必须是同步函数,因为我们前面说了,Mutation 其实是一个追踪记录,当 Mutation 触发的时候,回调函数还没有被调用,如果我们把异步请求放到这里,就会导致我们没办法实时的追踪状态,任何回调函数中的状态都是不可被追踪记录的
  2. Mutation 需遵守 Vue 的响应式规则(不知道的可以自己去官网看一下,这里简单的说一句)
  3. 最好提前在你的 store 中初始化好所有所需属性,就像我们在组件中的 data 一样,我们在改变的时候,最好先定义一下
  4. 因为 Vue 的响应规则,如果不是定于过的,就需要 Vue.set(obj, 'dog', 'rrrr')

在组件中使用 Mutations

第一步,定义注册,export dog 模块里面包含 state 还有 Mutations,当然,后面的 action 我们也可以这样:

enter image description here

后面的使用也是很简单的,注意我截图中的细节。

第二步,组件中调用

直接使用 this.$store.commit('xxx') 提交 Mutation:

enter image description here

使用辅助函数 mapMutations

enter image description here

第三步,页面可以做显示更新数据,最后我们可以在辅助工具里面看到结果:

enter image description here

可以很明显的看到我们的 name 改变了两次。

enter image description here

到这里相信大家都知道我们的 Mutatons 是怎么做的了,或许刚开始概念性的东西很绕,但是至少我们会用了,不是么!大家可以尝试着改变一下 age 来体验一下啦。

那么一个问题来了,既然 Mutation 只能是同步函数,那我异步请求的该怎么办呢?别急~~~我们接着走起。

action

action 其实和 Mutation 很像的,不论是定义的方式还是调用时候的辅助函数等,最大的亮点不同官网也说了:Action 提交的是 Mutation,而不是直接变更状态Action 可以包含任意异步操作

我们先来定义一个 actions,因为是异步操作,所以我会模拟 1 个 http 请求来假装异步了。哈哈哈,不过还是可以让大家感受到这是真的一个请求!!!

第一步,定义

与 Mutation 不同的是,action 的第一个参数接收一个上下文对象 context,起承上启下的作用,而 context 的属性和方法,是和 store 一模一样的,但是它并不是 store 对象,具体可以查看官网解释,context 中我们常用的就是 commit,来提交一个 Mutation,就像 redux 中的一样,action 只是发起了一个动作,然后我们用定时器模拟异步操作。

enter image description here

第二步,组件中分发使用

与我们的 Mutation 在组件中使用的时候,是通过 this.$store.commit('....') 来使用的类似,我们 action 在组件中使用的时候,是执行了分发的动作,具体来看看,或许直接说的话,不然咋对分发这个词不怎么很喜欢呢?

enter image description here

等待 2 秒之后,发现 name 改变,响应的 Mutation 也改变了,这是因为我们的 action 发起了一个动作,Mutaion 是动作的执行者,毕竟,大家都看到,我可没有在 action 做小动作来改变 name,所有改变 name 的动作都给了 Mutation,可不能冤枉 action。

enter image description here

发现没!!!!是不是和 Mutation 调用差不多呢,只不过我们的 Mutation 是同步的,是 commit,而我们的 action 是异步,我们可以在任何时候去调用,如果你切换及时的 2 秒后你就会看到你的 name 变成了 action 中定义的那个默认值咯,当然,你也可以传参进去新的值,覆盖默认的值,我们用辅助函数看看。

注意:辅助函数是需要 import 提钱引进来的;调用的时候不用写 dispatch,已经集成了,dispatch 本身返回的就是一个 promise,所以可以在 then 里面做一点事情~~~

enter image description here

是不是很惊喜的发现,辅助函数也是可以的呢,不过我咋感觉直接调用和通过辅助函数的时间不一样呢?或许大家可以测试一下哦~看看结果~~这个 name 是辅助函数传参进去的,取代了 action 中定义的默认值。

enter image description here

是不是发现,action 也会了呢~~~来让我们看一个稍微完整版的 action(带模拟请求):

enter image description here

当然,更多的复杂异步操作小伙伴们也可以结合各种新语法来实现,比如 async/await、以及 ES6 的解构赋值我想大家都应该知道,可以试着把 action 中的 context 用解构赋值替换一下啊~~~

getter

有时候,我们需要从 store 中派生出一些状态,比如狗狗的名字进行一个过滤,大于 2 岁的分组,小于 18 岁的分组等等,这个时候就需要我们对 store 来派生一个 getters,就像官方所说,你可以理解为,getters 是 store 的一个计算属性,话不多说,我们直接定义:

enter image description here

看到我们定义的一个过滤了么,判断名字是不是等于狗狗,然后在不同的生命周期去获取,可能会有很大的用处呢~~~

当然调用的时候也是 so easy 啦。

enter image description here

enter image description here

看!getters 就这么简单。

总结:

其实如果大家理解了这个状态管理的模式,就会发现,本身的调用并不是问题,甚至可以说很简单,而难处理的还是我们普通的数据,就像 getters 的这个过滤,定义、调用,没什么可以说的甚至,但是过滤的过程呢?文中只是判断了一下简单的相等,如果异步请求/同步更新中发生改变,页面一个列表数组的各种操作结合起来,多个组件共享一个数据中心的时候,或许才能感受到 Vuex 的强大之处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值