第十六节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。