Vuex新手入门指南-基本介绍

很多人在学习完 Vue.js 之后还会看到一个经常被提及的词语叫做 Vuex。

Vuex 字面上看与 Vue.js 只差了一个字母,但是他们两个做的事情完全不一样。

在本文我会像之前的 Vue.js 新手入门指南文章一样的问答形式来写文章。

1.Vuex 是什么?

我们还是像以往一样先看一看官方文档对此的解读(Vuex 是什么? · GitBook)

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

是不是又遇到了很多看起来很高大上听起来却一脸懵逼的专业术语?别急别急,我们慢慢来剖析一下这个 Vuex 究竟是个啥东西,他能做些啥。

2.Vuex 到底用来做什么?

用通俗一点的话来说,Vuex 就是一个用于管理 SPA 项目(不知道 SPA 是什么?请参考本专栏代码之美 - 知乎专栏中的历史文章)中状态的开源产品。

接下来又引出了一个问题,什么是状态,为什么要用 Vuex 这个东西去管理它?

3.什么是状态?为什么要去管理它?

状态这个东西其实我们生活中随处可见。我们头顶上的灯就有两种状态,一种是开,一种是关。状态说白了就是灯这个对象的的某个属性的值。

如果你对状态和属性这两个概念还是有所不了解,那么我打一个其他的比方吧。

我们平时是否有玩过王者荣耀或者英雄联盟 LOL 之类的网游?这些游戏里面每一个英雄当前都有生命值,法力值,攻击力,法术强度,护甲和魔抗等等,这些是这个英雄的属性,也就是英雄这个对象当前的状态。

属性分为固定属性和可变属性,一般像 LOL 里面大部分 ADC 英雄如果没有特殊的被动或者其他装备的支持,那么它的的攻击距离都是固定的,这个就是固定属性,这种固定属性的状态由于正常情况下都是不变的,所以我们可以直接写死在代码中(这种写死在代码中的变量的值称为硬编码),但是像其他的攻击力法术强度等等都是随装备和等级变化,那么这种属性是可变属性。

这些属性的状态由于会根据用户的各种操作(比如说出装备,打怪升级升级)变化。在传统的 Vue.js 的组件化开发中,一般这些状态都是分散在各个组件中,此时此刻如果两个英雄互相打起来了,那么就得分别去不同的组件中取状态值,然后进行状态值的修改,最后还要互相读取对方的状态值。如果他们本身是父子组件,那么还可以通过事件触发或者 Prop 属性来传递状态,但是如果是不同的组件,由于由于 Vue.js 本身组件之间有作用域,它们无法直接相互通信,所以就需要一些东西例如 Vuex 去集中管理和追踪它的变化。(如果你现在还是不明白这一大段话,可以好好回顾一下官方文档中组件 - vue.js 非父子组件通信 这个部分的内容)

在游戏中,这些状态一般以变量的形式保存在内存中,但是由于用户玩游戏的时候并不是直接去使用内存管理工具查看他们在内存里面的值,而是通过游戏界面去看这些值,所以还需要像 Vue.js 这种 MVVM 框架将状态同步到视图中。这就是 Vue.js 和 Vuex 之间的关系了。

4.什么情况下我应该使用 Vuex?

官方文档(Vuex 是什么? · GitBook)中说:

虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 global event bus 就足够您所需了。但是,如果您需要构建是一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它。

他的意思其实就是如果开发的程序并不是很庞大,一个页面中的组件不是很多并且他们之间并不需要大量频繁的互相读写操作,那么就可以直接使用传统的 Vue.js 中的组件 Prop 或者事件触发来修改状态,或者用组件 - vue.js#非父子组件通信 中介绍的 new 一个空的 Vue 对象实例,并且通过事件触发等方式来跨组件通信。

否则的话还是建议使用 Vuex。虽然 Vuex 本身需要有一段时间的学习成本,但是这个学习成本相对于你开发时期使用传统非父子组件通信机制遇到的各种坑来说还是比较划算的。这个就看你自己的权衡了。

5.Vuex 怎么安装和使用?

在前面讲解 Vue.js 入门的时候,我们用的是 Vue-Cli 这个脚手架工具来搭建的,由于这个脚手架工具本身会帮我们配置好 npm 的 package.json 文件,这个文件里面包含了这个 Vue.js 项目中所有依赖的包。

但是默认情况下这个脚手架工具没有为我们将 Vuex 这个依赖包给包含进去,所以我们得自己去“声明”一下我们这个 Vue.js 项目中需要依赖 Vuex 这个包。

我们该怎么“声明”呢?现在有两种办法:

一种是直接修改 package.json,这种方法看起来有点复杂,很多新手怕一不小心修改出错,可能会导致整个 package.json 文件结构出错,影响以后项目的依赖安装。

还有一种方法比较安全,只需要一行命令:

npm install vuex –save

表示安装 vuex 这个包,–save 表示将这个依赖包与本项目的依赖关系写入 package.json 中。

然后我们仅仅安装了这个依赖包是没有用的,我们还得在之前 Vue-Cli 为我们自动构建好的项目文件中的 main.js 主入口文件的开头里面加上两行这样的代码:

import Vuex from ‘vuex’Vue.use(Vuex)

第一行是用 ECMA6 的 import 将 vuex 包导入进来(这个是不是和 java 中导入 jar 包以及 php 中导入命名空间很相似?)

第二行是 Vue.js 本身的插件注入语法(参考官方文档插件 - vue.js),将插件注入 Vue.js 的目的是方便我们在插件内部调用它。

6.官方文档的五大核心概念是什么?

打开官方文档(Introduction · GitBook)能看到五大核心概念,他们都是啥?看了半天官方文档我还是对它们没什么了解,楼主能不能以通俗易懂的方式讲解一下它们的作用?当然可以啦!

首先先看一遍这个代码,不需要你看懂它。

const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }})7.State(状态)

官方文档:State · GitBook

这个状态就是前面所介绍的“状态”值的存放处。

看第 6 节末尾的代码中,状态就是在 state 属性中以键值对的形式声明这个 SPA 中所有的状态。上面的代码中声明了一个 count 状态。

之所以要在这里声明所有状态的原因,一是让代码更加优雅,如果你接手你同事的项目的时候,能够一眼从 Vuex 的状态声明中看出这个应用中有哪些状态,肯定开发效率杠杠滴。二是如果在这里声明了状态,那么 Vuex 就能够追踪到这个状态的变化。那么 Vue.js 中就可以在视图中对这个状态做出响应。

读取状态当然也是直接读取这个属性里面的各种子属性了。

8.Getter(获取器)

官方文档:Getters · GitBook

这个获取器和一些后端开发中模型层 ORM 中的获取器其实是差不多的功能。

比如说后端返回给我们的是一个 int 类型的时间戳,我们想把这个时间戳转换成正常人类可读的文本型时间表现形式(比如说 2017 年 3 月 11 日 12:43:31),那么我们就得在所有获取该状态的代码中增加一个转换函数。

可是现在有了状态获取器之后,我们可以统一将这个时间戳转字符串的函数写在获取器里面,要调用的时候就直接调用获取器就好了。

还有一些其他场景也可以使用获取器,比如说像错误码这种东西一般都是一个数字码对应一个文字形式的错误原因,我们也可以通过获取器来实现通过错误码拉取错误原因的功能。

使用获取器的方法则是直接调用 Vuex 实例的 getter 下的各种函数即可。

9.Mutations(转变)

官方文档:Mutations · GitBook

这个 Mutations 其实国内目前也没有比较好的翻译,通常我们都是直接称 Mutations。

我们前面只讲了可以通过调用 Vuex 的实例的 state 属性或者 getter 获取器来读取状态。但是没讲到如何修改状态。

官方文档中已经讲了需要先在 Vuex 实例的 Mutations 下编写对应的修改函数来修改状态。并且要修改的时候,要通过 Vuex 实例的 commit 方法来提交修改。也就是说任何对 state 状态的修改操作都必须写在 Mutations 中,并且还得用 Vuex 实例的 commit 来提交修改操作,并且由于 Mutations 函数可以传入参数,所以 commit 同理也可以传入参数。

这个时候可能有一些同学就会提问了,前面既然讲到了读取可以直接访问 Vuex 实例的 state 属性,为什么修改却不能直接去操作 Vuex 实例的 state 呢?官方文档和其他高手的回答是:

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

相当于我们通过一个 Mutations 函数可以显式的在代码中告诉开发者,我们这个 SPA 中究竟会对状态进行哪些操作,操作方式是什么。并且在后期我们使用一些辅助开发工具,可以保存状态的快照,就像 git 或者 svn 一样可以回滚状态。如果你还是有点不明白,总之你就按照官方文档说的去做吧,等开发一段时间之后会慢慢明白作者的良苦用心的,哈哈。

还有一个问题就是为什么状态修改的提交必须通过 Vuex 实例的 commit 方法提交呢?为什么不能直接调用 Mutations 函数呢?除了上面官方文档中提到的原因,网上还有高手解释了:因为 Vue.js 的状态修改其实是在内部有一个修改队列,通过 commit 的方式提交修改可以保证状态的修改是有序的。

10.Actions(动作)

官方文档:Actions · GitBook

前面提到了 Mutations 中可以对状态进行操作,但是忘记告诉各位同学,Mutations 中对状态的操作只能是同步操作,不能是异步操作。

如果这个时候我们有一个对状态的修改操作是异步的怎么办呢?

首先看看什么是异步操作?比如说 ajax 就可以选择是否发起异步请求,发起异步请求之后,我们就需要在回调函数里面进行请求结果的处理。关于 Java 异步的知识大家可以先使用各种搜索引擎自学一下。

现在回到 actions 上来,看看官方文档(Actions · GitBook):

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。

  • Action 可以包含任意异步操作。

其实异步的状态修改本质上还是通过几个同步操作组合的,所以我们还是得先声明好 mutation 同步操作方法,然后在 actions 中进行异步操作。如果我们暂时手头上没有 ajax 接口用于异步请求,那么我们可以像官方文档一样用 setTimeout 这种最简单的测试方法来理解。

actions: { incrementAsync ({ commit }) { setTimeout(() => { commit(‘increment’) }, 1000) }}

前面讲到了 mutation 是用 commit 来提交操作的,那么 actions 是怎么提交的呢?官方文档中说了 actions 是使用 Vuex 实例的 dispatch 方法来提交(其实说分发会更加准确)的。

至于其他更详细的 actions 操作官方文档讲的还是比较清楚的,没有什么比较复杂的概念,可以参考官方文档学习,这里不做更多讲解。

至于后面“组合 actions”中提到的 Promise 对象以及 async / await 都是 Java 中的一些特性,大家可以利用搜索引擎进行更多了解。

11.Modules(模块)

官方文档:Modules · GitBook

如果你的 SPA 项目非常的庞大,那么状态可能本身还需要进行分模块分类管理,这个时候就需要用到模块了。官方文档中已经给出了比较详细的模块操作代码,这里不再做更多讲解。

至于前面在将 actions 的时候,官方文档中说 actions 方法在声明的时候需要带上一个 context 参数,原因在这里可以得到解答:

对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState

12.严格模式

官方文档:严格模式 · GitBook

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

前面提到了 state 的修改需要通过提交 Mutations 或者分发 Actions,但是事实上我直接修改 state 可以吗?当然也是可以的,但是在开发阶段,为了尽可能防止开发者直接修改,就可以通过严格模式来检测这种错误的修改方式,并且抛出异常。

但是官方文档后面也提到了:

不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。

因此不要在生产环境下开启严格模式导致性能损失。

结语:

Vuex 综合来看是一个非常适合在 Vue.js 中使用的状态管理工具,当然类似的状态管理工具也有很多,比如说 React 的 Redux。

但是我们为了能够尽可能保证项目稳定性以及学习曲线的平滑,推荐在 Vue.js 中使用 Vuex。


文章作者: xkloveme
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xkloveme !
评论
 上一篇
公司Git实用记录 公司Git实用记录
一、git 命令名词解释 1、添加/跟踪/暂存:添加到本地索引 git add 文件名 2、提交:提交到本地仓库 git commit -m ‘注释’ 3、推送:将提交到本地仓库的所有更新提交到服务器 git push mycode rel
2017-08-23
下一篇 
Vue2.0父子组件之间和兄弟组件之间的数据交互 Vue2.0父子组件之间和兄弟组件之间的数据交互
熟悉了 Vue.js 的同级组件之间通信,写此文章,以便记录。Vue 是一个轻量级的渐进式框架,对于它的一些特性和优点,请在官网上进行查看,不再赘述。使用 NPM 及相关命令行工具初始化的 Vue 工程,目录结构如下 接着我们进入 Dem
2017-08-17
  目录