在上篇文章 从零搭建 Vue 开发环境 中,学习了 Vue 的语法,如何使用 Vue 进行开发,学会了如何搭建开发环境,打包部署等;文章中也介绍了兄弟组件之间传值是通过 Vuex 来实现的,只不过是进行了简单的应用,今天就来详细的深入学习如何使用 Vuex 来进行状态管理。
简介Vuex 是专门为 Vue.js 设计的状态管理库,它集中存储,管理所有组件的状态;通过上篇文章的学习,我们知道父组件要把值传递给子组件的时候,可以通过 props 来传递,子组件要把值传递给父组件的时候,可以通过事件的形式来实现,而对于兄弟组件来说,就需要用到 Vuex 来实现了。也就是一个组件把值放入到 Vuex 中,另一个组件从中取值从而实现参数传递的效果。
一个应用中,Vuex 用一个 state 对象就包含了全部应用层级的状态,它作为一个“唯一数据源 (SSOT)”而存在。也就是说,每个应用将仅仅包含一个 store 实例。所以它应该是一个全局单例模式。
如何使用首先要执行 npm install vuex --save 命令安装 Vuex 然后在 src 下创建 store 文件夹,在 store 文件夹内创建 index.js 文件,就在 index.js 里写 Vuex 。
最后在 main.js 中进行注册即可。
import Vue from 'vue' import App from './App.vue' import store from './store' new Vue({ store, render: h => h(App) }).$mount('#app')一个完整的 store 的 index.js 文件如下:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ // 所有的状态 state: { userName: '张三', userAge: 2 }, // 改变状态的操作 mutations: { setUserName(state, aUserName) { state.userName = aUserName; }, setUserAge(state, aUserAge) { state.userAge = aUserAge; }, }, // 相等于 vue 的计算属性 getters: { getAgeOfNextYear(state) { return state.userAge += 1; } }, // 和 mutations类似, 支持异步操作 actions: { getAgeOfNextYear(context) { context.commit('getAgeOfNextYear'); } } }) export default store // 需要在 main.js 中注册一个完整的 store 的 index.js 文件主要有 state, mutations, getters 和 actions 4项内容,下面依次来说说每个项的作用和用法。
statestate 用来存放所有组件的状态,每个组件都可以读取和修改状态;但是,并不是每个状态都适合放在 state 里面,如果是单个组件私有的状态,最好还是作为组件的局部状态。放在 state 里面的状态一般是多个组件共享的。
const store = new Vuex.Store({ // 所有的状态 state: { userName: '张三', userAge: 2, address: '四川省成都市', job: 'Java' },放在state里面的状态,在组件中,怎么获取值呢,是通过 this.$store.state.xxx 来获取的,如下所示,获取这几个 state 的值,并输出到控制台上:
export default { data() { return { userName: this.$store.state.userName, userAge: this.$store.state.userAge, address: this.$store.state.address, job: this.$store.state.job, } }, created() { console.info(this.userName); console.info(this.userAge); console.info(this.address); console.info(this.job); } }控制台输出如下:
如果安装了 Vue 的 chrome 开发插件 Vue Devtools ,也可以看到 state 里面的状态值:
Getter在 Vuex 中,Getter 的作用类似于 Vue 的计算属性的概念,可以对 state 里面的值进行计算,从而在组件调用的时候,不用每个组件都要重新计算,有点像 Java 里面的公共方法一样。
Getter里面的方法的第一个参数必须为 state
比如,我们要计算 state 里面的 userAge 这个状态值,让它返回明年的年龄:
const store = new Vuex.Store({ // 所有的状态 state: { userName: '张三', userAge: 2, address: '四川省成都市', job: 'Java' }, getters: { getAgeOfNextYear(state) { return state.userAge += 1; } },在组件中通过 this.$store.getters.xxx 来调用,如下所示 :
export default { data() { return { userAge: this.$store.state.userAge, ageOfNextYear: this.$store.getters.getAgeOfNextYear, } }, created() { console.info(this.userAge); console.info(this.ageOfNextYear); } }控制台和 Devtools 输出如下:
可以看到,执行了 Getter 里面的方法之后,state 里面的值也改变了。
Mutation在组件中通过 this.store.state.xxx 来获取状态的值,但是怎么改变它的值呢?是不能直接通过 this.store.state.xxx = XXX 来改变状态值的,而是需要通过 Mutation 来改变的。
const store = new Vuex.Store({ state: { userName: '张三', userAge: 2, address: '四川省成都市', job: 'Java' }, mutations: { setUserName(state, aUserName) { state.userName = aUserName; }, setUserAge(state, aUserAge) { state.userAge = aUserAge; }, },在组件中通过 this.$store.commit('xxx', 'param') 来调用:
export default { data() { return { userName: this.$store.state.userName, userAge: this.$store.state.userAge, } }, created() { console.info(this.userName); console.info(this.userAge); // 改变名字和年龄 this.$store.commit('setUserName', '李四') this.$store.commit('setUserAge', 5) console.info(this.$store.state.userName); console.info(this.$store.state.userAge); } }控制台输出如下,可以看到改变已经生效了:
Vuex 的 store 中的状态是响应式的,也就是说当我们变更状态时,监视状态的 Vue 组件也会自动更新。 还有一点需要注意的是 Mutation 中的操作是同步的。
ActionAction 类似于 mutation,也是用来改变 state 中的状态值,不同的地方在于:
Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。mutations: { setUserName(state, aUserName) { state.userName = aUserName; }, setUserAge(state, aUserAge) { state.userAge = aUserAge; }, }, actions: { setUserName(context) { context.commit('setUserName'); }, setUserAge(context) { context.commit('setUserAge'); } }调用: this.$store.dispatch('xxx')
Module上面说了所有组件的状态都需要放在 state 中,试想一下,如果有很多状态需要维护,把所有的状态都放在 state 中,是不是不好维护?就像 Java 中也不会把所有的类写在一个包下,也是分包存放的,这样有利于阅读和维护。
Vuex 提供了 Module的概念,我们可以把 store 分割成多个模块(Module),每个 Module 都有自己的 state, mutations, getters 和 actions;这个 Module 可以根据功能模块来划分,也可以根据业务需求来划分。
const userManagerModule = { state: { userId: '11111', userName: '张三', userAge: 2, }, mutations: { setUserName(state, aUserName) { state.userName = aUserName; }, setUserAge(state, aUserAge) { state.userAge = aUserAge; }, }, getters: {}, actions: {} } const goodsManagerModule = { state: { goodsName: '衣服', goodsPrice: 3, }, mutations: { setUserName(state, aUserName) { state.userName = aUserName; }, setUserAge(state, aUserAge) { state.userAge = aUserAge; }, }, getters: {}, actions: {} } const store = new Vuex.Store({ modules: { userManager: userManagerModule, goodsManager: goodsManagerModule } }) export default store // 需要在 main.js 中注册然后通过 this.$store.state.moduleName.xxx 来获取对应 Module 的状态。
export default { data() { return { userName: this.$store.state.userManager.userName, userAge: this.$store.state.userManager.userAge, goodsName: this.$store.state.goodsManager.goodsName, goodsPrice: this.$store.state.goodsManager.goodsPrice, } }, created() { console.info(this.userName); console.info(this.userAge); console.info(this.goodsName); console.info(this.goodsPrice); } }改变对应 Module 的状态值还是通过 this.$store.commit('xxx', 'param') 来实现:
export default { data() { return { userName: this.$store.state.userManager.userName, userAge: this.$store.state.userManager.userAge, goodsName: this.$store.state.goodsManager.goodsName, goodsPrice: this.$store.state.goodsManager.goodsPrice, } }, created() { console.info(this.userName); console.info(this.userAge); this.$store.commit('setUserName', '李四'); this.$store.commit('setUserAge', 5); console.info(this.$store.state.userManager.userName); console.info(this.$store.state.userManager.userAge); console.info(this.goodsName); console.info(this.goodsPrice); this.$store.commit('setGoodsName', '裤子'); this.$store.commit('setGoodsPrice', 6); console.info(this.$store.state.goodsManager.goodsName); console.info(this.$store.state.goodsManager.goodsPrice); } }可以看到 state 的状态都已经改变了。
但是如果多个 Module 之间的 mutations 中有同名的方法,执行 this.$store.commit('xxx', 'param') 后,哪个 Module 会生效呢?接下来看个例子:
const userManagerModule = { state: { userId: '11111', userName: '张三', userAge: 2, }, mutations: { setUserId(state, userId) { state.userId = userId; }, }, } const goodsManagerModule = { state: { userId: '22222', goodsName: '衣服', goodsPrice: 3, }, mutations: { setUserId(state, userId) { state.userId = userId; }, }, }现在两个 Module 都有 userId 属性,且分别为 11111 和 22222,此外,都有改变 userId 的方法 setUserId,控制台输出如下所示:
export default { data() { return { userManagerModuleUserId: this.$store.state.userManager.userId, goodsManagerModuleUserId: this.$store.state.goodsManager.userId, } }, created() { console.info(this.userManagerModuleUserId); console.info(this.goodsManagerModuleUserId); } }现在来改变 userId 的值为 88888,之后再次输出:
export default { data() { return { userManagerModuleUserId: this.$store.state.userManager.userId, goodsManagerModuleUserId: this.$store.state.goodsManager.userId, } }, created() { console.info(this.userManagerModuleUserId); console.info(this.goodsManagerModuleUserId); this.$store.commit('setUserId', '88888'); console.info(this.$store.state.userManager.userId); console.info(this.$store.state.goodsManager.userId); } }可以看到每个 Module 的 userId 都变了。
所以,分了 Module 之后,还需要注意这点,不然一个 Module 不小心修改了之后,出现问题了都不好排查。 这是因为默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
要解决这个问题,就需要使用命名空间 namespaced 来实现,在创建 Module 的时候,把 namespaced属性设置为 true,这样当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名
如下所示:
const userManagerModule = { namespaced:true, state: { userId: '11111', userName: '张三', userAge: 2, }, mutations: { setUserId(state, userId) { state.userId = userId; }, }, } const goodsManagerModule = { namespaced:true, state: { userId: '22222', goodsName: '衣服', goodsPrice: 3, }, mutations: { setUserId(state, userId) { state.userId = userId; }, }, }此时 state 的值为:
现在来改变每个 Module 的 userId 的值,通过
this.$store.commit('moduleName/functionName', 'param') 来实现。
export default { data() { return { userManagerModuleUserId: this.$store.state.userManager.userId, goodsManagerModuleUserId: this.$store.state.goodsManager.userId, } }, created() { console.info('修改之前 userManager模块的 userId = ' + this.userManagerModuleUserId); console.info('修改之前 goodsManager模块的 userId = ' + this.goodsManagerModuleUserId); // 改变 userManager 模块下的 userId 的值 this.$store.commit('userManager/setUserId', '44444'); // 改变 goodsManager 模块下的 userId 的值 this.$store.commit('goodsManager/setUserId', '88888'); console.info('修改之后 userManager模块的 userId = ' + this.$store.state.userManager.userId); console.info('修改之后 goodsManager模块的 userId = ' + this.$store.state.goodsManager.userId); } }控制台输出如下:
可以看到,现在每个 Module 只能修改自己内部的状态了。
通过使用命名空间 namespaced 就可以解决多个 Module 下 mutations 同名的问题啦。
同样,多个 Module 下的 Getter, Action 下的同名方法也是如此
Action: dispatch('moduleName/functionName') Getter: getters['moduleName/functionName'] 总结到这里 Vuex 的学习就结束啦,由于之前项目时间紧,大概学会了如何用 Vuex 就直接上手写代码了,没有深入了解,只是简单的 state 和 mutations,也没有分 Module,后面的业务中由于需要加更多的渠道进来,那后面就需要根据渠道来分 Module 了,每个渠道管理自己的状态。
此外,还了解了多个 Module 下同名的 mutations, Getter, Action 的处理方式,也算是避免了一个坑。
看来学习新知识还是要花时间去深入学习,才能写出易于维护,高质量的代码呀。
---来自腾讯云社区的---Java技术大杂烩
微信扫一扫打赏
支付宝扫一扫打赏