Vuex
一、简介
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
原理图:
安装:
// vue2 ==> vuex3.x vue3 ==> vue4.x
yarn add vuex@3
二、Vuex的环境搭建
1.创建文件:src/store/index.js
import Vuex from 'vuex';
import Vue from 'vue'
// actions - 响应组件中的动作 mvc中的service层
const actions = {}
// mutations - 用于操作数据(state) mvc中的dao层
const mutations = {}
// state - 存储数据
const state = {}
Vue.use(Vuex); // 需要在创建store实例前调用Vue.use(Vuex)
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
2.main.js中创建store配置项
import store from './store'
const vm = new Vue({
render: h => h(App),
store
});
vm.$mount('#root')
三、基本使用
1.初始化数据、配置actions
、配置mutations
,操作文件store.js
const actions = {
incre(context, value) { // context 类似于mini版的Store
context.commit('INCRE', value);
}
}
const mutations = {
INCRE(state, value) {
state.total += value;
}
}
const state = {
total: 0
}
Vue.use(Vuex);
export default new Vuex.Store({
actions,
mutations,
state
})
2.组件中读取vuex中的数据:$store.state.sum
<h2>当前求和为:{{ $store.state.total }}</h2>
3.组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)
或 $store.commit('mutations中的方法名',数据)
this.$store.dispatch('incre',this.operated_n);
this.$store.commit('DECRE', this.operated_n); //若没有网络请求或其他业务逻辑,可不写dispatch,直接编写commit
//另一种提交风格
this.$store.commit({
type:'DECRE',
id:'1'
})
// type就是提交的类型 其它为提交的参数
4.开发者工具
三、getters配置
简述:当state中的数据需要经过加工后再使用时,可以使用getters加工。
编码使用:在store.js
中追加getters
配置
// 用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.total*10; // vue 中的data
}
}
Vue.use(Vuex);
export default new Vuex.Store({ // vue computed
...
getters
})
组件中读取数据:$store.getters.bigSum
四、四个map方法的使用
1.mapState
用于帮助我们映射state
中的数据为计算属性
编码:
import { mapState } from "vuex";
coumputed:{
/* 原生
he() {
return this.$store.state.total;
}
*/
// 对象写法
...mapState({
he:'total',
n: (state) => state.n,
})
/* 原生
total() {
return this.$store.state.total;
}
*/
// 数组写法
...mapState(['total'])
}
2.mapGetters
用于帮助我们映射getters
中的数据为计算属性
编码:
import { mapGetters } from "vuex";
coumputed:{
/* 原生
dashu() {
return this.$store.getters.bigSum;
},
*/
// 对象写法
...mapGetters({
dashu:'bigSum'
})
/* 原生
bigSum() {
return this.$store.getters.total;
}
*/
// 数组写法
...mapGetters(['bigSum'])
}
3.mapMutations
简述:生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数
编码:
/* 原生
incre() {
this.$store.commit("INCRE", this.operated_n);
}
由 ...mapMutations生成的函数,需要在函数调用时传参
incre(value) {
this.$store.commit("INCRE", value);
}
*/
//对象写法
...mapMutations({
incre:'INCRE'
})
// 使用
<button @click="incre(1)">+</button>
//数组写法
...mapMutations(['INCRE'])
// 使用
<button @click="INCRE(1)">+</button>
4.mapActions
编码:
/* 原生
jia(){
this.$store.dispatch('incre',this.operated_n);
}
由 ...mapActions生成的函数,需要在函数调用时传参
jia(value) {
this.$store.dispatch("incre", value);
}
*/
//对象写法
...mapActions({
jia:'incre'
})
// 使用
<button @click="jia(1)">+</button>
//数组写法
...mapMutations(['incre'])
// 使用
<button @click="incre(1)">+</button>
5.辅助函数在setup中使用
在setup中使用mapState与mapGetters有变化,返回的函数使用了this,但setup中没有this,需要绑定this后,在使用computed函数包裹,当在外部使用,如用于事件的回调时,mapMutation,mapActions所返回的函数,可直接使用,不需要封装,因为不在setup函数中使用,当需要在setup中使用时,还是需要封装,改变this的指向,建议统一封装一下。
mapState、mapGetters使用:
import { useStore } from "vuex";
import { computed } from "vue";
import { mState,mGetters } from '../hooks/VuexAssist'
export default {
setup() {
const store = useStore();
// 从store中获取单个数据
const sCounter = computed(() => store.state.count);
const storeState = mState({counter:'count', n:'name'});
const storeGetters = mGetters(['bigCount']);
return {
sCounter,
...storeState,
...storeGetters
};
},
封装处理mapState在setup中使用:
import { computed } from "vue";
import { mapState, mapGetters, useStore } from "vuex";
const transformMap = (nativemap) => {
const store = useStore();
// 改变返回的函数的 this 指向,使其可以被调用,然后再传回computed
for (let key in nativemap) {
nativemap[key] = computed(nativemap[key].bind({ $store: store }));
}
return nativemap;
};
const mState = (mapSateInfo) => {
const mapResult = mapState(mapSateInfo);
return transformMap(mapResult);
};
const mGetters = (mapGettersInfo) => {
const mapResult = mapGetters(mapGettersInfo);
return transformMap(mapResult);
};
export { mState, mGetters };
mapMutations使用:
setup() {
const store = useStore();
const storeMutations = mapMutations(["increment"]);
const incre = () => {
// 将函数在setup中执行,需要改变this指向
storeMutations.increment.bind({$store: store})();
};
return {
...storeMutations, // 将函数在setup外执行,不需要改变this指向
incre
};
封装工具函数:
import { computed } from "vue";
import {
mapState,
mapGetters,
useStore,
mapActions,
mapMutations,
createNamespacedHelpers,
} from "vuex";
const transformFn = (isWapperComputed) => {
return (nativemap) => {
// 用于作用mapSate、mapGetters,改变this指向,并包裹computed
const store = useStore();
if (isWapperComputed) {
// state/getters需要包裹computed
for (let key in nativemap) {
nativemap[key] = computed(nativemap[key].bind({ $store: store }));
}
} else {
// actions/mutations不需要包裹computed
for (let key in nativemap) {
nativemap[key] = nativemap[key].bind({ $store: store });
}
}
return nativemap;
};
};
/**
*
* @param {*} mFn 需要转换的映射方法
* @param {*} mFnName 需要转换的方法名,用于获取模块化的映射方法
* @param {*} transformFn 返回 用于改变map返回的函数达的this指向的 方法
* @returns
*/
const mapperFn = (mFn, mFnName, transformFn) => {
// 函数柯里化
return (...args) => {
let mapper = null;
if (args.length === 1) {
// 只有一个参数时,就没有模块化获取数据,直接获取rootState的数据
mapper = args[0];
return transformFn(mFn(mapper));
}
if (args.length === 2) {
// 获取模块名称 映射信息
const module = args[0];
mapper = args[1];
if (typeof module === "string" && module.length > 0) {
// 判断模块名称是否存在
mFn = createNamespacedHelpers(module)[mFnName];
const mapResult = mFn(mapper);
return transformFn(mapResult);
} else {
throw new Error("first argument must be a string and not empty");
}
}
throw new Error("useSate must be called with one or two arguments");
};
};
const useState = mapperFn(mapState, "mapState", transformFn(true));
const useGetters = mapperFn(mapGetters, "mapGetters", transformFn(true));
const useMutations = mapperFn(mapMutations, "mapMutations", transformFn(false));
const useActions = mapperFn(mapActions, "mapActions", transformFn(false));
export { useState, useGetters, useMutations, useActions };
五、Vuex模块化+命名空间
简述:让多种数据分裂明确,代码更容易维护
修改store.js
import Vuex from 'vuex';
import Vue from 'vue';
// Count组件相关数据
const countOptions = {
namespaced:true, // 如果不起开,不能使用mapState等简写方式映射,只能this.$store.count.total
actions:{...},
mutations:{...},
state:{...},
getters:{...}
}
// Person组件相关数据
const personOptions = {
namespaced:true,
actions:{...},
mutations:{...},
state:{
personList: [
{ id: "001", name: "六七", age: 19 },
{ id: "002", name: "十三", age: 23 },
{ id: "003", name: "五四", age: 18 }
]
},
getters:{...}
}
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
person:personOptions,
count:countOptions
}
})
1. 读取state
//方式一:自己直接读取
this.$store.state.personAbout.list;
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']) // 前一个参数标识模块名称
2.读取getters
//方式一:
this.$store.getters['person/fitstPersonName']; // 写原生的,除了state,都是 ['person/fitstPersonName'] 形式获取
//方式二:借助mapGetters读取:
...mapGetters('person',['fitstPersonName'])
3.调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('count/incre',person)
//方式二:借助mapActions:
...mapActions('count',['incre'])
4.调用commit
//方式一:自己直接commit
this.$store.commit('person/ADDPERSON',personObj);
//方式二:借助mapMutations:
...mapMutations('person',['ADDPERSON']),
5.使用createNamespacedHelpers
获取某个模块的mapState、mapGetters、mapActions、mapMutations
<script>
import { createNamespacedHelpers } from "vuex";
const { mapState: home_mapState,mapMutations,mapGetters,mapActions } = createNamespacedHelpers("home");
export default {
computed: {
...home_mapState(["homeCounter"]),
},
};
</script>