背景
我在做一个页面效果,就是在首页上点击导航,或者其他的按钮需要在主显示区域创建一个tab,同时也要与面包屑的效果。开始我没有开通菜单的router效果,仅仅是利用菜单组价的点击事件做出了想要的效果,使用的是事件总线传参,因为事件总线可以在任何组件间传递参数,事件总线的使用可以参考我的《手把手学习Vue3.0:Vue3.0正确使用Bus总线mitt实现组件间通信和传参》。事件总线的特点是在事件发起的地方挂载事件,在需要跟随变化的接收方监听事件。之所以选择vuex是因为我发现时间总线方式在实现复杂场景的时候有弊端,下面跟我一起看看怎么使用vuex。
放弃总线的原因
还以我的页面效果举例子:
- 由于菜单组件开通了路由功能,所以每次点击操作有路由的更新
- tab组件和面包屑组件依赖菜单的变化,同时面包屑和tab组件还需要其他的属性才能实现效果
- 档切换tab的时候菜单和面包屑都要做相应的变化,这个时候就会多写事件,代码已经开始不美观,非结构性的代码开始出现了,不利于维护,场景越复杂就越乱。
- 而事件本身就是一个很纯粹的操作,为了实现效果事件变得不纯粹。
- 参数传递存在局限性,监听组件需要的参数不同
基于以上几点我觉得vuex更适合这样的场景,vuex是集中管理,全局单例唯一,而且vue将状态从根组件“注入”到每一个子组件中,使用起来非常方便。
Vuex的学习方法
vue3.x对应的vuex4.x版本,目前看还是纯英文网站,我推荐的学习方法是:
- 大家对照着3.x的中文文档先学会vuex有什么,怎么使用。
- 一定要从头到尾把3.x的中文档看完,并总结出自己的理解
- 3.x的目录跟4.x的目录是想对应的,大部分是相同的,差异化的可以直接翻译,英文好的直接读,不好的也可以读读看,锻炼下自己也可以。
- 代码部分直接看4.x,代码部分谁都能看的懂,有差别的地方格外注意下就OK。
- 时间长了,多看几遍英文也就那么回事了,最主要的有中英文对照,可以免费学英文。
- 一定要动手去尝试,哪怕出错也没关系,不动手永远不知道怎么运行时对的。手白了谁动手了,代码就是谁的。动手的根没有动手的差别非常大,尤其是一个人负责整个项目的时候。
Vuex的核心思想
状态管理的核心是如下三方面:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
接下来我谈一下个人对这三个核心的理解:
- state,就是我们交给vuex管理的全局数据
- view,就是把vuex中的state跟页面渲染绑定
- actions,就是操作state的API
按我的转义,大家心里可以有一个简单的整体理解,就是vuex提供放数据的地方,这些数据不推荐直接取,而是用它推荐的API去操作,像绑定其他变量一样使用,页面自动渲染。 实战分解部分,我会讲清楚,state写在什么地方,vuex的index.js里都提供什么,我们的组件中怎么使用。
Vuex的集成
- 单独安装命令
npm install vuex@next --save
- 如果是用cli脚手架生成的项目会自动集成,通常都在store目录下。
- index.js代码如下:
state:存在状态,数据区
mutations:更改state的唯一途径,定义提交commit的方法,所有的mutations都是同步方法
actions:提交mutation,不直接变更状态state,而是由mutation在更改state,action中可以包含异步操作,action可以组合和调用其他action,相当于把state的commit也管理起来了。对于使用state的组件来说关于state的使用都由vex独立管理起来。
modules:模块或者分组,如果项目很大,要vuex管理的状态很多,都写到一个文件有点臃肿,这个时候分不同的模块,单个文件就非常小,而且单一集中更好管理。一般情况下都写在index.js中就够了。
- 在main.js中引入store
- 以上代码都是脚手架自动生成的,直接拿来用就可以了,我的这些都是最新版本基于vue3.0的,cli4以后的。
- 到此vuex的集成就结束了。
Vuex的实战分解
实战我从两个方面讲解,一是vuex中都定义什么(即store中怎么编码),而是组件中都怎么使用。这些写法都是固定的套路,大家先背过,后面想怎么写就怎么写。
1.store中的固定写法:
import { createStore } from "vuex";
export default createStore({
state: {
todos: [
{ id: 1, text: "...", done: true },
{ id: 2, text: "...", done: false }
],
count: 0
},
getters: {
doneTodos(state) {
return state.todos.filter(todo => todo.done);
}
},
mutations: {
increment(state) {
// mutate state
state.count++;
}
},
actions: {
increment(context) {
context.commit("increment");
}
},
modules: {}
});
- state定义了两个状态todos、count
- getters是通过Getter 属性访问状态,这里可以包含一些逻辑,这里只有读,没有更新。在使用组件中Getter会缓存和同步更新。
- mutations是所有操作的定义,唯一途径。通过commit来调用
- actions是封装commit的抽象,这样commit对组件使用也透明了。
- modules暂时先不用关心,只是对store的切割,用法不变。
- 如果有面向对象的思维,就是数据定义和操作都给你封装好了,外部使用直接调用API即可,不需要关注内部细节,也不需要外部直接操作store。
2.组件中的具体使用写法:
- 在计算属性中获取状态值。这个时候上面定义的Getter就起作用了,可以使用辅助函数
mapState、mapGetters。vuex4.x跟vuex3.x在这块的代码一样没区别。
- 在任意method中可以提交更改,这是调用上面的action,通过
store.dispatch方法触发,可以使用辅助函数mapActions
![]()
如果上面没有定义上action,那么可以使用mapMutations,但是不推荐使用
实际使用中都是使用辅助函数+简写的方式,这样代码简洁,易于维护,推荐的方式
总结
- 一定要遵守约定由于配置的思想,让代码更优雅
- vuex的使用可以用来那个句话来说:1.store中写state、getters、mutations、actions。2.组件中使用辅助函数来获取和提交
mapState、mapGetters、mapMutations、mapActions- 庖丁解牛般的拆开了揉碎了,再拼起来,vuex怎么和其他的核心组件组合使用就一目了然了,而且也可以做到熟记于心。
- 如果不把核心抽象出来,脑子还是一锅粥,似乎理解,但又不知道从哪里开始说,或者组织不出有效的话语一下说清楚。
- 我的这篇文章就是要解决这个问题。这里的代码都是官方的例子,后面我会把我的代码开源出来。
- 如果喜欢一键三联,评论、点赞、加关注。