vue3.0使用mapState,mapGetters和mapActions的方式
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方法找到對(duì)應(yīng)模塊的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方法找到對(duì)應(yīng)模塊的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方法找到對(duì)應(yīng)模塊的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對(duì)vuex中mapState,mapGetters輔助函數(shù)封裝
1. readonly API的使用
在我們傳遞給其他組件數(shù)據(jù)時(shí),如果直接將響應(yīng)式數(shù)據(jù)傳遞給子組件。子組件如果使用數(shù)據(jù)不規(guī)范,修改了父組件傳進(jìn)來的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, ? ? }; }
開發(fā)中我們往往希望其他組件使用我們傳遞的內(nèi)容,但是不允許它們修改時(shí),就可以使用 readonly了。
// 父組件 // <ReadonlyChild :info="infoReadonly" /> setup() { ? ? const info = reactive({ ? ? ? name: "哇哈哈", ? ? }); ? ? const infoReadonly = readonly(info); ? ? const onChangeInfo = () => { ? ? ? // 在父組件中可修改info中的值,子組件依然可響應(yīng)更新 ? ? ? info.name = "父組件給你的值"; ? ? }; ? ? return { ? ? ? infoReadonly, ? ? ? onChangeInfo ? ? }; } // 子組件 setup(props) { ? ? const onChangeInfo = () => { ? ? ? const info = props.info; ? ? ? // 此時(shí)修改props時(shí),控制臺(tái)會(huì)有一個(gè)警告: ? ? ? // Set operation on key "name" failed: target is readonly.? ? ? ? info.name = "woowow"; ? ? }; ? ? return { ? ? ? onChangeInfo, ? ? }; }
2. 響應(yīng)式變量直接解構(gòu)會(huì)失去響應(yīng)性
將響應(yīng)式變量直接解構(gòu)會(huì)失去其響應(yīng)性
const info = reactive({ age: 18 }); // 直接解構(gòu)后 age 值失去響應(yīng)性,當(dāng) onChangeAge 函數(shù)觸發(fā)時(shí),age值不在變,而ageRef 依然具有響應(yīng)性 const { age } = info; const { age: ageRef } = toRefs(info); const onChangeAge = () => { ? info.age++; };
3. watchEffect 清除副作用
watchEffect API 可自動(dòng)收集依賴項(xiàng),當(dāng)依賴項(xiàng)改變時(shí)觸發(fā)偵聽器函數(shù)。當(dāng)我們?cè)趥陕犉骱瘮?shù)執(zhí)行額外的副作用函數(shù),例如:發(fā)送網(wǎng)絡(luò)請(qǐng)求時(shí)。每當(dāng)依賴性項(xiàng)變更都會(huì)發(fā)起一個(gè)新的網(wǎng)絡(luò)請(qǐng)求,那么上一次的網(wǎng)絡(luò)請(qǐng)求應(yīng)該被取消掉。這個(gè)時(shí)候我們就可以清除上一次的副作用了。
setup() { ? ? const count = ref(0); ? ? const onChangeCount = () => { ? ? ? count.value++; ? ? }; ? ? watchEffect((onInvalidate) => { ? ? ? // 偵聽器函數(shù)中需要發(fā)起網(wǎng)絡(luò)請(qǐng)求,用setTimeout模擬 ? ? ? const timer = setTimeout(() => { ? ? ? ? console.log("請(qǐng)求成功啦"); ? ? ? }, 2000); ? ? ? // 在偵聽器函數(shù)重新執(zhí)行時(shí)觸發(fā)onInvalidate函數(shù) ? ? ? onInvalidate(() => { ? ? ? ? // 在這個(gè)函數(shù)中清除請(qǐng)求 ? ? ? ? clearTimeout(timer); ? ? ? ? console.log("onInvalidate 回調(diào)觸發(fā)"); ? ? ? }); ? ? ? // 自動(dòng)收集count的依賴 ? ? ? console.log("count-在改變", count.value); ? ? }); ? ? return { ? ? ? count, ? ? ? onChangeCount, ? ? }; ? }
4. setup 函數(shù)訪問Vuex中Store數(shù)據(jù)
4.1 使用mapState輔助函數(shù)
通常需要通computed函數(shù)來獲取state中數(shù)據(jù),并保存響應(yīng)性。
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輔助函數(shù)得不到想要的結(jié)果 ? ? ?* 這樣獲取的storeState 是一個(gè) { name: ?function mappedState (){...}, age: function mappedState (){...}, height: function mappedState (){...} } 這樣的對(duì)象 ? ? ?*/ ? ? const storeState = mapState(["name", "age", "height"]); ? ? // 需要對(duì)返回值進(jìn)行處理 ? ? 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; }
在組件中使用時(shí)
import useState from "@/hooks/useState"; setup() { ? ? // 數(shù)組用法 ? ? const state = useState(["name", "age", "height"]); ? ? // 對(duì)象用法,可使用別名 ? ? const stateObj = useState({ ? ? ? uName: (state) => state.name, ? ? ? uAge: (state) => state.age, ? ? ? uHeight: (state) => state.height, ? ? }); ? ? return { ? ? ? ...state, ? ? ? ...stateObj, ? ? }; ? }
4.2 mapGetters 輔助函數(shù)的封裝
其原理與mapState 函數(shù)封裝類似
// 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兩個(gè)函數(shù)相似度很高,在進(jìn)一下封裝
// 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 對(duì)module的支持
useState 和 useGetters 函數(shù)暫時(shí)還不支持傳入命名空間,進(jìn)一步封裝。 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, ? } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Vue3使用Vuex之mapState與mapGetters詳解
- 記一次vuex的mapGetters無效原因及解決
- vuex中...mapstate和...mapgetters的區(qū)別及說明
- vuex 中輔助函數(shù)mapGetters的基本用法詳解
- vuex2中使用mapGetters/mapActions報(bào)錯(cuò)的解決方法
- vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
- 詳解vuex中mapState,mapGetters,mapMutations,mapActions的作用
- Vue Getters和mapGetters的原理及使用示例詳解
相關(guān)文章
vue+vue-fullpage實(shí)現(xiàn)整屏滾動(dòng)頁面的示例代碼(直播平臺(tái)源碼)
這篇文章主要介紹了vue+vue-fullpage實(shí)現(xiàn)整屏滾動(dòng)頁面,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06Vue單頁面應(yīng)用保證F5強(qiáng)刷不清空數(shù)據(jù)的解決方案
最近小編遇到這樣的問題當(dāng)vue單頁面按F5強(qiáng)刷,數(shù)據(jù)就恢復(fù)初始了,這怎么辦呢?下面小編給大家?guī)砹薞ue單頁面應(yīng)用保證F5強(qiáng)刷不清空數(shù)據(jù)的解決方案,感興趣的朋友一起看看吧2018-01-01vscode中prettier和eslint換行縮進(jìn)沖突的問題
這篇文章主要介紹了vscode中prettier和eslint換行縮進(jìn)沖突的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10詳解使用webpack打包編寫一個(gè)vue-toast插件
本篇文章主要介紹了詳解使用webpack打包編寫一個(gè)vue插件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11vue3使用element-plus搭建后臺(tái)管理系統(tǒng)之菜單管理功能
這篇文章主要介紹了vue3使用element-plus搭建后臺(tái)管理系統(tǒng)之菜單管理,使用element-plus el-tree組件快速開發(fā)樹形菜單結(jié)構(gòu),el-tree組件中filter-node-method事件便可以實(shí)現(xiàn)樹形菜單篩選過濾功能,需要的朋友可以參考下2022-04-04如何在vite初始化項(xiàng)目中安裝scss以及scss的使用
今天想要給vite項(xiàng)目,添加全局的scss變量文件引用,這樣我們?cè)谑褂胹css變量和函數(shù)的時(shí)候就不需要每個(gè)組件都取引用了,下面這篇文章主要給大家介紹了關(guān)于如何在vite初始化項(xiàng)目中安裝scss以及scss使用的相關(guān)資料,需要的朋友可以參考下2022-10-10