Vuex是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在组件这边对状态进行操作时,首先是分发到Actions然后再提交到Mutations,为什么不直接提交到Mutations?
因为在与服务器交互的时候可能存在异步的行为,如果存在异步行为一定要通过Dispatch分发到Actions然后再提交到Mutations,Actions中声明的方法是异步的,Mutations中声明的方法是同步的,这Vuex的一个约定,修改状态的唯一方法就是提交Mutations。
什么情况使用Vuex?
Vuex 可以帮助我们管理共享状态(这个数据属性想共享在所有组件中使用),并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
Vuex的基本使用
安装
在已经创建的项目中通过vue add vuex进行安装
使用Vue-Cli3创建项目
终端输入vue create 项目名,选择Babel、Router、Vuex就行。
(*) Babel
>(*) Router
(*) Vuex
项目目录
在当前项目目录下相比vue-router项目多了一个store文件夹。同时,在main.js中进行一个导入import store from './store'并在new Vue({store})进行挂载,然后在store目录下的index.js中对vuex进行一个导入import Vuex from 'vuex'并使用插件Vue.use(Vuex),最后创建并抛出:
export default new Vuex.Store({
state: { //当前的状态
},
mutations: { //声明同步的方法
},
actions: { //声明异步的方法
},
modules: {}
})
在state中声明我们想要共享的数据,然后在组件中的computed方法中通过this.$store.state.数据名进行获取:
computed: {
count() {
return this.$store.state.count;
},
},
使用vuex来进行一个计数操作
在About.vue中声明方法,通过this.$store.dispatch("increate");触发actions中的方法,然后在actions中commitmutations中的方法,在mutations的方法中通过state.count++;对状态进行修改。
如果修改state中的状态是不是异步操作,可以直接commit这个mutations中的方法,如果涉及到了异步操作,一定要先dispatch这个actions中的方法再commit。
store下的index.js:
关于actions中第一个参数的解构,第一个参数是context官方运行上下文,获取当前的vuex实例,只不过这边使用的时候通过解构取值获取{commit} = context
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: { //当前的状态
count: 0
},
mutations: { //声明同步的方法
increate(state) {
//修改状态
state.count++;
},
decrease(state) {
state.count--;
},
//异步方法 需要在actions中声明
/* increateAsync(state) {
setTimeout(() => {
state.count++;
}, 1000)
} */
increateAsync(state) {
state.count++;
}
},
actions: { //声明异步的方法
//解构赋值 获取 commit
//非异步操作可以直接 commit mutations中的方法
/* increate({
commit
}) {
// commit mutations中的方法
commit('increate')
},
decrease({
commit
}) {
commit('decrease')
} */
//异步操作
increateAsync({
commit
}) {
setTimeout(() => {
commit('increateAsync')
}, 1000)
}
},
modules: {}
})
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
{{count}}
<button @click="increate">+1</button>
<button @click="decrease">-1</button>
<button @click="increateAsync">+1(异步)</button>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
},
},
methods: {
increate() {
// dispatch 触发 actions 中声明的方法
//this.$store.dispatch("increate");
//因为这两个方法是同步的,所以可以直接 commit
this.$store.commit("increate");
},
decrease() {
//this.$store.dispatch("decrease");
this.$store.commit("decrease");
},
increateAsync() {
this.$store.dispatch("increateAsync");
},
},
};
</script>
Vuex的辅助函数
在项目中有很多的状态,在组件中如果每一个都通过像计数操作中this.$store.state.count;这样去获取,会很麻烦,为了解决这个问题,vuex提供了辅助函数。
首先需要在组件中对辅助函数进行一个导入
//导出vuex中的辅助函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
mapState
之前我们是在computed属性中通过count() {return this.$store.state.count;},这种方式进行一个获取,有了辅助函数后可以配合es6中的扩展运算符...进行一个非常简便的获取。
- 在当前组件中如果computed中的属性名和state中的状态名一样,可以通过下面这种方式获取state中定义的
count和username状态。
...mapState(["count", "username"]),
- 在当前组件中如果computed中的属性名和state中的状态名不一样,则需要通过下述方式获取,其中
myCount和user就是当前组件定义在computed中的属性,count和username则是state种定义的状态。
...mapState({
myCount: "count",
user: "username",
}),
mapGetters
在index.js中new Vuex.Store({})除了常用的4个属性之外,其实还有可选的getters,在getters中可以定义一些方法,然后在组件那边可以通过mapGetters进行获取。
index.js
getters: {
evenOrOdd(state) {
return state.count % 2 === 0 ? '偶数' : '奇数';
}
},
About.vue
export default {
computed: {
//不用辅助函数获取
/* evenOrOdd() {
return this.$store.getters.evenOrOdd;
}, */
//使用mapGetters获取
...mapGetters(["evenOrOdd"]),
},
mapMutations
mapMutations则是对同步方法进行获取。对于之前计数操作中的同步方法:
methods: {
/* increate() {
// dispatch 触发 actions 中声明的方法
//this.$store.dispatch("increate");
//因为这两个方法是同步的,所以可以直接 commit
this.$store.commit("increate");
}, */
/* decrease() {
//this.$store.dispatch("decrease");
this.$store.commit("decrease");
}, */
//获取同步方法
...mapMutations(["increate", "decrease"]),
},
mapActions
mapActions是对异步方法进行获取。对于之前计数操作中的异步方法:
methods: {
/* increateAsync() {
this.$store.dispatch("increateAsync");
}, */
//获取异步方法
...mapActions(["increateAsync"]),
},
在组件内提交数据给Vuex
dispatch、commit内传参
在About.vue中触发函数的时候同时进行传参,可以是字符串、数组、对象,一般情况下传递对象。
// 传参 方式一
increateAsync() {
//在组件内部提交数据 载荷形式分发
this.$store.dispatch("increateAsync", { amount: 10 });
},
// 传参 方式二
increateAsync() {
this.$store.dispatch({
type: "increateAsync",
amount: 10,
});
},
上述两种方式均可,然后在index.js中需要进行第二个参数的接收
mutations: { //声明同步的方法
increateAsync(state, amount) {
state.count += amount;
}
},
actions: { //声明异步的方法
//异步操作
increateAsync({
commit
}, {
amount
}) {
setTimeout(() => {
commit('increateAsync', amount)
}, 1000)
}
},
辅助函数传参
对于辅助函数来说也能够进行一个参数的传递,只不过它需要在绑定click事件的时候就进行一个传参。
<button @click="increateAsync(amount)">+1(异步)</button>
Vuex模块化
当项目足够大时,在index.js的state属性中就可能需要定义很多的状态,这会使得代码显得非常臃肿,难以阅读,不易维护。Vuex给我们提供了一个modules属性,运行模块化创建,每一个模块都维护它自己的state、mutations和actions等。
模块化步骤
- 在store文件夹下创建一个
modules文件夹,对每一个模块在modules文件夹下分别创建对应的js文件。
cart.js
export default {
//命名空间
namespaced: true,
state: {
cartList: [],
count: 0
},
getters: {},
mutations: {},
actions: {}
}
此时当前模块和index.js还没有任何关系。
- 将创建的模块导入到index.js中,并将导入模块挂载到
modules属性中
//导入
import cart from './modules/cart';
import products from './modules/products';
//挂载
modules: {
cart,
products
}
命名空间
为了模块的维护性和复用性,可以在模块中添加namespaced: true,使其成为带命名空间的模块,当模块被注册后,它的所有getter、actions及mutations都会自动根据当前模块注册的路径调整命名。对于模块内的state,模块内的状态已经是嵌套的了,使用namespaced属性不会对其产生影响,命名空间主要是针对于模块中的辅助函数起作用。
在组件中获取模块数据
cart.js模块中定义了
getters: {
getCount(state) {
return state.count;
}
},
- 首先在组件中进行辅助函数导入
//导入辅助函数
import { mapGetters } from "vuex";
- 通过辅助函数获取模块中定义的方法
这边与之前稍有不同,第一个参数需要指明是哪个模块,比如下面从cart模块中获取getters中的getCount方法。
computed: {
...mapGetters("cart", ["getCount"]),
},