携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情
概括
官方定义:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我的理解:将不同组件需要共用的数据集中保存在一起,避免了嵌套组件传参的麻烦,需要使用时可以直接调用或者通过计算得到,并且是响应式的。
什么是“状态管理模式”?
假如现在有一个大型单页面的项目,由很多组件和子组件构成,当某个数据发生变化时,这些页面要做出相应的反应,这就是多个组件共享一个状态,此时就会产生两个问题:
- 多个视图依赖同一个状态
- 来自不同视图的行为需要变更同一个状态
虽然都有对应的解决办法,但是对于中大型项目来说,嵌套组件的传参过于繁琐,解决起来还是不太优雅,维护也很困难。
所有就想到了把这些共享的状态提取出来,用一个全局单例模式管理,任何组件都能直接访问到这些状态,状态发生变化时,这些组件也能实时更新。
附上一张官方的流程图:
安装VueX
先使用脚手架创建一个 Vue 项目,可以通过脚手架直接安装 VueX,也可以通过下面的步骤手动搭建。
终端执行 npm i vuex -s ,然后创建 src/store/index.js 文件,文件内容如下:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state:{},
getters:{},
mutations:{},
actions:{},
modules:{}
});
在 main.js 文件中:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store"; //引入的store对象
Vue.config.productionTip = false;
new Vue({
router,
store, //把store对象绑定到Vue实例上,每个实例只能有一个store对象
render: (h) => h(App),
}).$mount("#app");
到这步就安装配置完成了,接下来就可以使用,在上面的store对象中,有五个对象属性,,这五个对象属性也正是VueX的核心。
State
state就是存放数据的仓库,在state中添加属性
state:{
price:5,
//……任何你想共享的状态
}
接下来在组件中获取这个状态,在 AboutView.vue 中:
<template>
<div class="about">
{{ price }}
</div>
</template>
computed:{
price(){
return this.$store.state.price;
}
}
由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态。
Mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,每个mutation 都有一个字符串的事件类型和一个回调函数。
mutations:{
//type:‘change_price’ 回调函数
change_price(state, price) {
state.price = price;
},
}
在 AboutView.vue 中:
<input type="number" v-model="sub_price" @change="change_price" />
data() {
return {
sub_price: 0,
};
},
methods: {
change_price() {
this.$store.commit("change_price", this.sub_price);
},
},
在input框中绑定了一个 sub_price 属性,当它被修改时调用 change_price 方法,在这个方法中提交了一个 mutation,将会调用 mutations 中的 change_price 方法,并在回调函数中接收 sub_price,修改 state 中 price 的值。
Actions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
actions: {
// context:与 store 实例具有相同方法和属性的对象
dec_price(context, payload) {
setTimeout(() => {
//提交 mutations
context.commit("dec_price", payload);
}, 1000);
},
},
mutations: {
change_price(state, price) {
state.price = price;
},
dec_price(state, price) {
state.price += price;
},
},
在 AboutView.vue 中:
<button @click="dec_price">price减1</button>
dec_price() {
//通过dispatch调用action中的方法
this.$store.dispatch("dec_price", -1);
},
每次点击按钮后,会经过大约一秒后把price减1,在 Action 中主要实现一些异步的操作。
Modules
当项目足够大时,在 state 中管理的状态就会变得臃肿,可以把 store 分割成模块,每个模块都有自己的 state、mutation、action、getter,甚至模块中还能嵌套模块。
const moduleA = {
namespaced: true,
state: () => ({
count: 10,
}),
mutations: {
add_count(state, payload) {
state.count += payload;
},
},
actions: {},
getters: {},
};
export default new Vuex.Store({
modules: {
moduleA: moduleA,
},
});
在 AboutView.vue 中:
{{ price }}*{{ count }} =
<button @click="add_count(1)">加数量</button>
<button @click="add_count(-1)">减数量</button>
computed:{
//返回模块 state 中的状态
count(){
return this.$store.state.moduleA.count;
}
}
methods:{
add_count(val){
//通过命名空间提交模块的 mutation
this.$store.commit("moduleA/add_count",val)
}
}
接下来就可以在页面中通过数量加减按钮控制模块中 state 的状态了。
Getters
可以理解为 store 中的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
const moduleA = {
getters: {
/**
* @description:根据价格数量计算总价
* @param {*} state 当前模块的state
* @param {*} getters
* @param {*} rootState 根模块的state
* @return {*}
*/
total(state, getters, rootState) {
return state.count * rootState.price;
},
},
};
在 AboutView.vue 中:
{{ price }}*{{ count }} = {{ total }}
computed: {
total() {
//调用模块中 getters 的 total 方法
return this.$store.getters["moduleA/total"];
},
},
小结
对于 vuex 算是入了个门,后面需要更多的实践操作才能理解更加深入,mapState 和 mapGetters 辅助函数比较简单,就没有展开细说,还有 modules 中的命名空间,只需要添加一个属性 namespaced: true, 即可。vuex 状态持久化也比较简单,可以自己保存到 localStorage 或者 sessionStorage 中,也可以直接使用 vuex-persistedstate 插件。