vue3.0使用mapState,mapGetters和mapActions的方式
目录
- vue2.0中使用mapState及mapActions的方式
- vue3.0中获取state和使用actions的方式
- 如何才能在vue3下使用mapState这些api呢?
- vue3对vuex中mapState,mapGetters辅助函数封装
- 1. readonly API的使用
- 2. 响应式变量直接解构会失去响应性
- 3. watchEffect 清除副作用
- 4. setup 函数访问Vuex中Store数据
vue2.0中使用mapState及mapActions的方式
// 使用mapState computed: { ...mapState({ //... }) } methods: { ...mapActions(['fnA', 'fnB']) }
vue3.0中获取state和使用actions的方式
import {computed} from 'vue' import {useStore} from 'vuex' setup() { const store = useStore(); const stateA = computed(() => store.state.stateA); const stateB = computed(() => store.state.stateB); const methodA = store.dispatch('methodA', {name: '张三'}); }
如何才能在vue3下使用mapState这些api呢?
答案是封装mapState,mapGetters,mapActions方法。
1、新建useMapper.js
import { useStore } from 'vuex' import { computed } from 'vue' export function useStateMapper(mapper, mapFn) { const store = useStore(); const storeStateFns = mapFn(mapper); const storeState = {}; Object.keys(storeStateFns).forEach(fnKey => { // vuex源码中mapState和mapGetters的方法中使用的是this.$store,所以更改this绑定 const fn = storeStateFns[fnKey].bind({ $store: store }); storeState[fnKey] = computed(fn) }) return storeState } export function useActionMapper(mapper, mapFn) { const store = useStore(); const storeActionsFns = mapFn(mapper); const storeAction = {}; Object.keys(storeActionsFns).forEach(fnKey => { storeAction[fnKey] = storeActionsFns[fnKey].bind({ $store: store }) }) return storeAction }
2、新建useState.js
import { mapState, createNamespacedHelpers } from 'vuex' import { useStateMapper } from './useMapper' import {checkType} from './index' /** * * @param {*} moduleName 模块名称 * @param {*} mapper state属性集合 ['name', 'age'] * @returns */ export function useState(moduleName, mapper) { let mapperFn = mapState; // 如果使用模块化,则使用vuex提供的createNamespacedHelpers方法找到对应模块的mapState方法 if (checkType(moduleName) === "[object String]" && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapState } return useStateMapper(mapper, mapperFn) }
3、新建useGetters.js
import { mapGetters, createNamespacedHelpers } from 'vuex' import { useStateMapper } from './useMapper' import {checkType} from './index' /** * * @param {*} moduleName 模块名称 * @param {*} mapper getters属性集合 ['name', 'age'] * @returns */ export function useGetters(moduleName, mapper) { let mapperFn = mapGetters; // 如果使用模块化,则使用vuex提供的createNamespacedHelpers方法找到对应模块的mapGetters方法 if (checkType(moduleName) === "[object String]" && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapGetters } return useStateMapper(mapper, mapperFn) }
4、新建useActions.js
import { mapActions, createNamespacedHelpers } from 'vuex'; import {useActionMapper} from './useMapper' import {checkType} from './index' /** * * @param {*} moduleName 模块名称 * @param {*} mapper 方法名集合 ['fn1', 'fn2'] * @returns */ export function useActions(moduleName, mapper) { let mapperFn = mapActions; // 如果使用模块化,则使用vuex提供的createNamespacedHelpers方法找到对应模块的mapActions方法 if (checkType(moduleName) === "[object String]" && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapActions } return useActionMapper(mapper, mapperFn) }
5、页面中使用
<template> <div class="home"> <span>姓名:{{name}} 年龄:{{age}} 性别:{{sex}}</span> <button @click="changeName">改名</button> </div> </template>
<script> // @ is an alias to /src import {useState} from '@/utils/useState' import {useActions} from '@/utils/useAction' export default { name: "home", setup() { const storeState = useState('home', ['name', 'age', 'sex']) const storeActions = useActions('home', ['setName']) const changeName = () => { storeAction.setName('李四') } return { changeName, ...storeState, ...storeActions }; }, }; </script>
vue3对vuex中mapState,mapGetters辅助函数封装
1. readonly API的使用
在我们传递给其他组件数据时,如果直接将响应式数据传递给子组件。子组件如果使用数据不规范,修改了父组件传进来的props值没有任何反馈。
// 父组件 // <ReadonlyChild :info="info" /> setup() { const info = reactive({ name: "哇哈哈", }); return { info, }; } // 子组件 setup(props) { const onChangeInfo = () => { const info = props.info; // 修改父组件传来的props 没有任何反馈。 info.name = "woowow"; }; return { onChangeInfo, }; }
开发中我们往往希望其他组件使用我们传递的内容,但是不允许它们修改时,就可以使用 readonly了。
// 父组件 // <ReadonlyChild :info="infoReadonly" /> setup() { const info = reactive({ name: "哇哈哈", }); const infoReadonly = readonly(info); const onChangeInfo = () => { // 在父组件中可修改info中的值,子组件依然可响应更新 info.name = "父组件给你的值"; }; return { infoReadonly, onChangeInfo }; } // 子组件 setup(props) { const onChangeInfo = () => { const info = props.info; // 此时修改props时,控制台会有一个警告: // Set operation on key "name" failed: target is readonly. info.name = "woowow"; }; return { onChangeInfo, }; }
2. 响应式变量直接解构会失去响应性
将响应式变量直接解构会失去其响应性
const info = reactive({ age: 18 }); // 直接解构后 age 值失去响应性,当 onChangeAge 函数触发时,age值不在变,而ageRef 依然具有响应性 const { age } = info; const { age: ageRef } = toRefs(info); const onChangeAge = () => { info.age++; };
3. watchEffect 清除副作用
watchEffect API 可自动收集依赖项,当依赖项改变时触发侦听器函数。当我们在侦听器函数执行额外的副作用函数,例如:发送网络请求时。每当依赖性项变更都会发起一个新的网络请求,那么上一次的网络请求应该被取消掉。这个时候我们就可以清除上一次的副作用了。
setup() { const count = ref(0); const onChangeCount = () => { count.value++; }; watchEffect((onInvalidate) => { // 侦听器函数中需要发起网络请求,用setTimeout模拟 const timer = setTimeout(() => { console.log("请求成功啦"); }, 2000); // 在侦听器函数重新执行时触发onInvalidate函数 onInvalidate(() => { // 在这个函数中清除请求 clearTimeout(timer); console.log("onInvalidate 回调触发"); }); // 自动收集count的依赖 console.log("count-在改变", count.value); }); return { count, onChangeCount, }; }
4. setup 函数访问Vuex中Store数据
4.1 使用mapState辅助函数
通常需要通computed函数来获取state中数据,并保存响应性。
setup() { const store = useStore(); // 在setup中要获取store中的state。如果state非常多,无疑这样做很繁琐 const uName = computed(() => store.state.name); const uAge = computed(() => store.state.age); const uHeight = computed(() => store.state.height); /** * 直接使用mapState辅助函数得不到想要的结果 * 这样获取的storeState 是一个 { name: function mappedState (){...}, age: function mappedState (){...}, height: function mappedState (){...} } 这样的对象 */ const storeState = mapState(["name", "age", "height"]); // 需要对返回值进行处理 const resStoreState = {}; Object.keys(storeState).forEach((fnKey) => { const fn = storeState[fnKey].bind({ $store: store }); resStoreState[fnKey] = computed(fn); }); return { uName, uAge, uHeight, ...resStoreState, }; }
封装成hooks如下:
// useState.js import { computed } from "vue"; import { useStore, mapState } from "vuex"; export default function useState(mapper) { const store = useStore(); const storeStateFns = mapState(mapper); const storeState = {}; Object.keys(storeStateFns).forEach((fnKey) => { const fn = storeStateFns[fnKey].bind({ $store: store }); storeState[fnKey] = computed(fn); }); return storeState; }
在组件中使用时
import useState from "@/hooks/useState"; setup() { // 数组用法 const state = useState(["name", "age", "height"]); // 对象用法,可使用别名 const stateObj = useState({ uName: (state) => state.name, uAge: (state) => state.age, uHeight: (state) => state.height, }); return { ...state, ...stateObj, }; }
4.2 mapGetters 辅助函数的封装
其原理与mapState 函数封装类似
// useGetters.js import { computed } from "vue"; import { mapGetters, useStore } from "vuex"; export default function useGetters(mapper: any) { const store = useStore(); const storeGettersFns = mapGetters(mapper); const storeGetters = {}; Object.keys(storeGettersFns).forEach((fnKey) => { const fn = storeGettersFns[fnKey].bind({ $store: store }); storeGetters[fnKey] = computed(fn); }); return storeGetters; }
useState和useGetters两个函数相似度很高,在进一下封装
// useMapper.js import { computed } from "vue"; import { useStore } from "vuex"; export default function useMapper(mapper, mapFn) { const store = useStore(); const storeStateFns = mapFn(mapper); const storeState = {}; Object.keys(storeStateFns).forEach((fnKey) => { const fn = storeStateFns[fnKey].bind({ $store: store }); storeState[fnKey] = computed(fn); }); return storeState; } // useState.js import { mapState } from "vuex"; import useMapper from "./useMapper"; export default function useState(mapper) { return useMapper(mapper, mapState); } // useGetters.js import { mapGetters } from "vuex"; import useMapper from "./useMapper"; export default function useGetters(mapper: any) { return useMapper(mapper, mapGetters); }
4.3 对module的支持
useState 和 useGetters 函数暂时还不支持传入命名空间,进一步封装。 useMapper的封装保持不变。
// useState.js import { createNamespacedHelpers, mapState } from "vuex"; import useMapper from "./useMapper"; export default function useState(mapper, moduleName) { let mapperFn = mapState; if (typeof moduleName === "string" && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapState; } return useMapper(mapper, mapperFn); } // useGetters.js import { createNamespacedHelpers, mapGetters } from "vuex"; import useMapper from "./useMapper"; export default function useGetters(mapper, moduleName) { let mapperFn = mapGetters; if (typeof moduleName === "string" && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapGetters; } return useMapper(mapper, mapperFn); } // 在组件中的使用 // Home.vue setup() { const state = useState(["homeCounter"], "home"); const stateGetter = useGetters(["doubleHomeCounter"], "home"); return { ...state, ...stateGetter, } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。
赞 (0)