第十六节:Vuex4.x 简介及state、getters、mutations、actions详解(OptionApi 和 CompositionApi)
一. Vuex简介
1. 简介
(官网地址:https://next.vuex.vuejs.org/zh/index.html 在Vue2中使用的详见:https://www.cnblogs.com/yaopengfei/p/14571316.html 本节采用的版本号【4.0.2】)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的store模式(或者父子组件传值、缓存等)就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

2. 核心总结
在Vuex中,有五大核心模块,分别是state、getters、mutations、actions、module,下图是每个模块的作用,以及在OptionApi 和 Composition Api中对应的 $store 、辅助函数的两种写法。

3. 快速入门
(1). 在store文件夹中创建index.js文件
import { createStore } from 'vuex'
// 导入相关常量
import { ADD_N } from './mutation-type'
export default createStore({
state() {
return {
counter1: 100,
name: 'ypf',
age: 18,
height: '1.82m'
}
},
getters: {
// 第一个参数是固定参数state, 用来获取上面state中的对象,
ageInfo(state) {
return `我的年龄是${state.age}`;
},
//可以返回一个函数,来实现传参
// 外界传过来的值这里用msg接收,这里的getters主要是用来获取其它getter属性的
nameInfo(state, getters) {
return function(msg) {
return `我的名字是${state.name},${msg},${getters.ageInfo}`;
}
},
},
mutations: {
increment(state) {
state.counter1++;
},
decrement(state) {
state.counter1--
},
/*
参数说明:
state:用来获取上面state中的数据
payLoad:用于接收外界传递过来的数据
*/
// payLoad可以是int类型,表示递增数
incrementN(state, payLoad) {
state.counter1 += payLoad;
},
// payLoad可以是对象,比如 {myStep:20,name:'ypf'}
decrementN(state, payLoad) {
state.counter1 -= payLoad.myStep;
},
// payLoad可以是int类型,表示递增数
[ADD_N](state, payLoad) {
state.counter1 += payLoad;
}
},
actions: {
/*
两个参数:
(1). context:是一个和$store实例均有相同方法和属性的对象,可以从其中获取到commit方法来提交一个mutation,
或者通过 context.state 和 context.getters 来获取 state 和 getters
可以解构为:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界传递过来的数据,可以是各种类型
*/
incrementAction(context, payLoad) {
setTimeout(() => {
context.commit('increment');
}, 1000);
},
// payLoad可以是一个对象 {myStep:20,myStep2:30}
decrementNAction({ commit, dispatch, state, rootState, getters, rootGetters }, payLoad) {
if (payLoad.myStep) {
commit('decrementN', payLoad);
} else {
commit('decrement');
}
},
// 通过promise实现捕获异常的结束
testAdd(context) {
return new Promise((resolve, reject) => {
try {
context.commit('increment');
resolve('执行结束啦')
} catch (e) {
reject('出错了')
}
});
}
},
modules: {}
})
(2). 在入口文件main.js中进行引入
import { createApp } from 'vue'
// 需要测试不同页面
import App from './pages/09_action_optionApi/App.vue'
// 引入Vuex插件
import store from './store'
createApp(App).use(store).mount('#app')
二. state详解
1. 作用
State 提供唯一的公共数据源,所有共享的数据都要统一放到 State 中进行存储。
export default createStore({ state() { return { counter1: 100, name: 'ypf', age: 18, height: '1.82m' } }, })
2. Options Api中用法
写法1: $store对象
<h4>counter1:{{$store.state.counter1}}</h4> <h4>name:{{$store.state.name}}</h4>
写法2:辅助函数mapState
参数有两种:①数组类型,原名映射 ②对象类型,自定义名
<template> <div> <!-- 2. mapState函数的用法 --> <p>2. mapState函数的用法</p> <h4>counter1:{{counter1}}</h4> <h4>name:{{name}}</h4> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> import { mapState } from 'vuex' export default { computed: { // 1. 写法1-原名映射(数组类型) ...mapState(['counter1', 'name']), // 2. 写法2-自定义名(对象类型) ...mapState({ myCounter1: state => state.counter1, myName: state => state.name }) } } </script>
3. Composition Api中用法
写法1: 通过useStore创建store对象
<template> <div> <!-- 1. 通过useStore拿到store后去获取某个状态即可 --> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> import { mapState, useStore } from 'vuex'; import { computed } from 'vue'; export default { setup() { // 方案1-通过useStore拿到store后去获取某个状态即可 const store = useStore(); const myCounter1 = computed(() => store.state.counter1); const myName = computed(() => store.state.name); return { myCounter1, myName } } } </script>
写法2: 自己封装useState方法 【推荐!】
useState.js代码
import { useStore, mapState } from 'vuex'
import {computed} from 'vue'
/*
封装在setup中获取state中的值
①:params: 需要获取的参数名, 可以是数组,也可以是对象
分别对应两种调用方式
*/
export function useState(params) {
const store = useStore();
const storeStateFns = mapState(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
使用代码
<template> <div> <!-- 调用封装后代码 --> <h4>counter1:{{counter1}}</h4> <h4>name:{{name}}</h4> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> // 导入封装 import { useState } from '../../hooks/version1/useState' export default { setup() { // 写法1:传递数组 const result1 = useState(['counter1', 'name']); // 写法2:传递对象 const result2 = useState({ myCounter1: state => state.counter1, myName: state => state.name }); return { ...result1, ...result2 } } } </script>
三. getters详解
1. 作用
getters用于对 State 中的数据进行加工处理形成新的数据。
① getters 可以对 State 中已有的数据加工处理之后形成新的数据,类似 Vue 的Computed。
② State 中数据发生变化,getters 的数据也会跟着变化。
注意:getters中声明的是方法;
方法的第一个参数为state,可以获取State中属性。可以返回一个函数,来实现传参。
export default createStore({ state() { return { counter1: 100, name: 'ypf', age: 18, height: '1.82m' } }, getters: { // 第一个参数是固定参数state, 用来获取上面state中的对象, ageInfo(state) { return `我的年龄是${state.age}`; }, //可以返回一个函数,来实现传参 // 外界传过来的值这里用msg接收,这里的getters主要是用来获取其它getter属性的 nameInfo(state, getters) { return function(msg) { return `我的名字是${state.name},${msg},${getters.ageInfo}`; } }, }, })
2. Options Api中用法
写法1: $store对象
<h4>{{$store.getters.ageInfo}}</h4> <h4>{{$store.getters.nameInfo('哈哈哈') }}</h4>
写法2:辅助函数mapState
参数有两种:①数组类型,原名映射 ②对象类型,自定义名
<template> <div> <!-- 2.辅助函数调用 --> <h4>{{ageInfo}}</h4> <h4>{{nameInfo('呵呵呵')}}</h4> <h4>{{myAgeInfo}}</h4> <h4>{{myNameInfo('嘿嘿嘿')}}</h4> </div> </template> <script> import { mapGetters } from 'vuex'; export default { computed: { // 辅助函数调用 // 写法1-传递数组(原名映射) ...mapGetters(['ageInfo', 'nameInfo']), // 写法2-传递对象(可以自定义名称) ...mapGetters({ myAgeInfo: 'ageInfo', myNameInfo: 'nameInfo' }) } } </script>
3. Composition Api中用法
写法1: 通过useStore拿到store后去获取某个getters即可(不推荐,繁琐)
<template> <div> <!-- 1.通过useStore拿到store后去获取某个getters即可--> <h4>{{ageInfo2}}</h4> <h4>{{nameInfo2('嘿嘿嘿嘿1')}}</h4> </div> </template> <script> // 原始写法的引入 import { computed } from 'vue'; import { useStore } from 'vuex'; export default { setup() { // 1. 通过useStore拿到store后去获取某个getters即可(不推荐) var store = useStore(); const ageInfo2 = computed(() => store.getters.ageInfo); const nameInfo2 =computed(()=>store.getters.nameInfo);
return { ageInfo2, nameInfo2, } } } </script>
写法2: 自己封装useGetters方法
useGetters.js封装
import { useStore, mapGetters } from 'vuex'
import {computed} from 'vue'
/*
封装在setup中获取getters中的值
①:params: 需要获取的参数名, 可以是数组,也可以是对象
分别对应两种调用方式
*/
export function useGetters(params) {
const store = useStore();
const storeStateFns = mapGetters(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
调用
<template> <div> <!-- 2.调用自己的封装 --> <h4>{{ageInfo}}</h4> <h4>{{nameInfo('哈哈哈哈1')}}</h4> <h4>{{myAgeInfo}}</h4> <h4>{{myNameInfo('哈哈哈哈2')}}</h4> </div> </template> <script> // 自己封装的引入 import { useGetters } from '../../hooks/version1/useGetters.js'; export default { setup() {// 2. 自己封装(推荐) // 调用1-传递数组类型 var result1 = useGetters(['ageInfo', 'nameInfo']); // 调用2-传递对象类型 var result2 = useGetters({ myAgeInfo: 'ageInfo', myNameInfo: 'nameInfo' }); return { ageInfo2, nameInfo2, ...result1, ...result2 } } } </script>
补充state和getters抽离共同点代码后,统一封装:
useMapper.js
import { useStore } from 'vuex'
import {computed} from 'vue'
/*
抽离useState和useGetters中的通用逻辑
①:params: 需要获取的参数名, 可以是数组,也可以是对象
分别对应两种调用方式
②:fn:可以是mapGetters 或 mapState
*/
export function useMapper(params,fn) {
const store = useStore();
const storeStateFns = fn(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
useState.js
import { useStore, mapState } from 'vuex'
import { useMapper } from './useMapper'
/*
封装在setup中获取state中的值
①:params: 需要获取的参数名, 可以是数组,也可以是对象
分别对应两种调用方式
*/
export function useState(params) {
return useMapper(params, mapState);
}
useGetters.js
import { useStore, mapGetters } from 'vuex'
import { useMapper } from './useMapper'
/*
封装在setup中获取getters中的值
①:params: 需要获取的参数名, 可以是数组,也可以是对象
分别对应两种调用方式
*/
export function useGetters(params) {
return useMapper(params, mapGetters);
}
四. mutations详解
1. 作用
mutations 用于变更State中 的数据。
① 只能通过 mutation 变更 State中 数据(actions也需要context.commit来间接的修改state),不可以直接操作 Store 中的数据。(禁止 通过 this.$store.state.count 获取后直接修改)
② 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
注:mutations中声明方法,方法的第一个参数为state,可以获取State中属性,第二个参数可以自定义类型,进行传递。
mutation-type.js
// 自定义常量 export const ADD_N = 'add_n';
import { createStore } from 'vuex'
// 导入相关常量
import { ADD_N } from './mutation-type'
export default createStore({
state() {
return {
counter1: 100,
}
},
mutations: {
increment(state) {
state.counter1++;
},
decrement(state) {
state.counter1--
},
/*
参数说明:
state:用来获取上面state中的数据
payLoad:用于接收外界传递过来的数据
*/
// payLoad可以是int类型,表示递增数
incrementN(state, payLoad) {
state.counter1 += payLoad;
},
// payLoad可以是对象,比如 {myStep:20,name:'ypf'}
decrementN(state, payLoad) {
state.counter1 -= payLoad.myStep;
},
// payLoad可以是int类型,表示递增数
[ADD_N](state, payLoad) {
state.counter1 += payLoad;
}
},
})
2. Options Api中用法
写法1: $store对象
<h4><button @click="$store.commit('increment')">加1</button></h4> <h4><button @click="$store.commit('incrementN',20)">加N</button></h4>
写法2:辅助函数mapMutations
参数有两种:①数组类型,原名映射 ②对象类型,自定义名
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <p>2.mapMutations用法</p> <h4><button @click="decrement">减1</button></h4> <h4><button @click="decrementN({myStep:100})">减N</button></h4> <!-- 自定义名称 --> <h4><button @click="myIncrement">加1</button></h4> <h4><button @click="myIncrementN(200)">加N</button></h4> </div> </template> <script> import { mapMutations } from 'vuex'; export default { methods: { // 辅助函数 // 写法1-传递数组 ...mapMutations(['decrement', 'decrementN']), // 写法2-传递对象 ...mapMutations({ myIncrement: 'increment', myIncrementN: 'incrementN' }), } </script>
补充:可以在自定义的方法你进行$store对象的调用、$store对象的特有对象调用、调用mapMutations中的方法
export default { methods: {// 当然也可以自己封装个方法 test1() { // this.$store.commit('increment'); // 或 // this.myIncrementN(2); // 补充一个参数为对象类型的时候的特有写法 // type属性中写方法名,其它属性则为传递的对象属性哦 // this.$store.commit({ // type: 'decrementN', // myStep: 50, // name:'ypf' // }) // 补充使用自定义常量 this.$store.commit(ADD_N, 1000); } }, }
3. Composition Api中用法
写法1:
通过useStore创建store对象,这里的store对象就是OptionApi中的$store,然后使用commit方法进行调用。
写法2.
直接调用mapMutations辅助函数,然后进行返回即可。
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <h4><button @click="decrement">减1</button></h4> <h4><button @click="decrementN({myStep:100})">减N</button></h4> <h4><button @click="myIncrement">加1</button></h4> <h4><button @click="myIncrementN(200)">加N</button></h4> <h4><button @click="add_n(200)">加N</button></h4> </div> </template> <script> import { mapMutations } from 'vuex'; // 导入自定义常量 import { ADD_N } from '../../store/mutation-type.js' export default { setup() { // setup中调用辅助函数mapMutations容易,直接调用即可,不用再自己封装了 const result1 = mapMutations(['decrement', 'decrementN']); const result2 = mapMutations({ myIncrement: 'increment', myIncrementN: 'incrementN' }); // 使用自定义常量的方式 const result3 = mapMutations([ADD_N]); return { ...result1, ...result2, ...result3 } } } </script>
五. actions详解
1. 作用
如果通过异步操作变更数据,必须通过 actions,而不能使用 mutations,但是在 action 中还是要通过触发Mutation 的方式间接变更数据。
注:actions中的方法有两个参数
(1). context:是一个和$store实例均有相同方法和属性的对象,可以从其中获取到commit方法来提交一个mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters, 可以解构为:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界传递过来的数据,可以是各种类型
import { createStore } from 'vuex'
export default createStore({
actions: {
/*
两个参数:
(1). context:是一个和$store实例均有相同方法和属性的对象,可以从其中获取到commit方法来提交一个mutation,
或者通过 context.state 和 context.getters 来获取 state 和 getters
可以解构为:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界传递过来的数据,可以是各种类型
*/
incrementAction(context, payLoad) {
setTimeout(() => {
context.commit('increment');
}, 1000);
},
// payLoad可以是一个对象 {myStep:20,myStep2:30}
decrementNAction({ commit, dispatch, state, rootState, getters, rootGetters }, payLoad) {
if (payLoad.myStep) {
commit('decrementN', payLoad);
} else {
commit('decrement');
}
},
// 通过promise实现捕获异常的结束
testAdd(context) {
return new Promise((resolve, reject) => {
try {
context.commit('increment');
resolve('执行结束啦')
} catch (e) {
reject('出错了')
}
});
}
},
})
2. Options Api中用法
写法1. $store对象
<h4><button @click="$store.dispatch('incrementAction')">加1(延迟1s)</button></h4> <h4><button @click="$store.dispatch('decrementNAction',{myStep:100})">减N</button></h4>
写法2. 辅助函数mapActions
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <p>2.mapActions用法</p> <h4><button @click="incrementAction">加1(延迟1s)</button></h4> <h4><button @click="decrementNAction({myStep:100})">减N</button></h4> <!-- 自定义名称 --> <h4><button @click="myincrementAction">加1(延迟1s)</button></h4> <h4><button @click="mydecrementNAction({myStep:100})">减N</button></h4> <p>3.在自己封装的方法中调用</p> <button @click="test1">test1</button> </div> </template> <script> import { mapActions } from 'vuex'; export default { methods: { // 辅助函数用法 // 1. 用法1-传递数组 ...mapActions(["incrementAction", "decrementNAction"]), // 2. 用法2-传对象(自定义名称) ...mapActions({ myincrementAction: "incrementAction", mydecrementNAction: "decrementNAction" }), test1() { // 1.$store也可以在自己定义的方法中使用哦 // this.$store.dispatch('incrementAction'); //2. 补充$store对象的特殊调用方式 this.$store.dispatch({ type: 'decrementNAction' }); // 或 // this.$store.dispatch({ // type: 'decrementNAction', // myStep:101 // }); } }, data() { return {}; } } </script>
补充:可以在自定义的方法你进行$store对象的调用、$store对象的特有对象调用、调用mapActions中的方法
3. Composition Api中用法
写法1:
通过useStore创建store对象,然后调用dispatch方法
<template> <div> <h3>{{$store.state.counter1}}</h3> <p>1. 通过useStore拿到store后,然后调用dispatch进行分发</p> <h4><button @click="tADD">加1(延迟1s)</button></h4> </div> </template> <script> import { useStore } from 'vuex'; export default { setup() { // 方案1 通过useStore拿到store后,然后调用dispatch进行分发 const store = useStore(); const tADD = () => { store.dispatch('incrementAction'); }; return { tADD, } } } </script> <style scoped> </style>
写法2:
调用辅助函数mapActions,直接返回即可
<template> <div> <h3>{{$store.state.counter1}}</h3> <p>2.mapActions用法</p> <h4><button @click="incrementAction">加1(延迟1s)</button></h4> <h4><button @click="decrementNAction({myStep:100})">减N</button></h4> <!-- 自定义名称 --> <h4><button @click="myincrementAction">加1(延迟1s)</button></h4> <h4><button @click="mydecrementNAction({myStep:100})">减N</button></h4> <p>3. 测试捕获异步的结束</p> <h4><button @click="tTest">测试捕获异常结束</button></h4> </div> </template> <script> import { mapActions, useStore } from 'vuex'; export default { setup() {//方案二:使用辅助函数 (推荐!! 简洁) // 在setup中直接使用辅助函数即可,不需自己再封装了 const actions1 = mapActions(["incrementAction", "decrementNAction"]); // (自定义名称) const actions2 = mapActions({ myincrementAction: "incrementAction", mydecrementNAction: "decrementNAction" }); // 测试捕获异步的结束 const tTest=()=>{ store.dispatch('testAdd').then(res=>{ console.log(`获取异步结束后的返回信息:${res}`); }); }; return { ...actions1, ...actions2, tTest } } } </script>
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。

浙公网安备 33010602011771号