Vue3 直接使用Vuex的mapState和mapGetters时报错的分析及解决方案
Vuex 提供了 mapState、mapGetters、mapActions 和 mapMutations 四个函数,其返回结果分别是 mappedState,mappedGetter,mappedAction 和 mappedAction 四个函数。
使用方式如下 store/index.js
import { createStore } from 'vuex'const state = {msg: 'hello world'
}const getters = {newMsg: state => `Hi~ ${state.msg}`
}export default createStore({state, getters
})
一、获取 state 中 msg 和 getters 中 newMsg 的方法
1、直接获取
<script setup>import { useStore } from 'vuex'const $store = useStore()console.log($store.state.msg) // 'hello world'console.log($store.getters.newMsg) // 'Hi~ hello world'
</script>
2、计算属性方式获取
<script setup>import { computed } from 'vue'import { useStore } from 'vuex'const $store = useStore()// 计算属性返回的 msg 是一个 ComputedRefImpl 引用对象// 模板以外的地方使用时,需要 ".value"const msg = computed(() => $store.state.msg) console.log(msg.value) // 'hello world'// 计算属性返回的 newMsg 是一个 ComputedRefImpl 引用对象// 模板以外的地方使用时,需要 ".value"const newMsg = computed(() => $store.getters.newMsg) console.log(newMsg.value) // 'Hi~ hello world'
</script>
3、使用 mapState 和 maGetters 方式获取
vue3 组件(选项式风格)
import { useStore, mapState, mapGetters } from 'vuex'
...
setup() {const $store = useStore()// mapState 返回的 msg 是一个 mappedState 函数let { msg } = mapState(['msg']) // 计算属性返回的 msg 是一个 ComputedRefImpl 引用对象msg = computed(msg) console.log(msg.value) // 报错// mapGetters 返回的 newMsg 是一个 mappedGetter 函数let { newMsg } = mapGetters(['newMsg']) // 计算属性返回的 newMsg 是一个 ComputedRefImpl 引用对象newMsg = computed(newMsg) console.log(newMsg.value) // 报错return {msg, newMsg}
}
...
vue3 组件(组合式风格)
<script setup>import { computed } from 'vue'import { useStore, mapState, mapGetters } from 'vuex'const $store = useStore()// mapState 返回的 msg 是一个 mappedState 函数let { msg } = mapState(['msg']) // 计算属性返回的 msg 是一个 ComputedRefImpl 引用对象msg = computed(msg) console.log(msg.value) // 报错// mapGetters 返回的 newMsg 是一个 mappedGetter 函数let { newMsg } = mapGetters(['newMsg']) // 计算属性返回的 newMsg 是一个 ComputedRefImpl 引用对象newMsg = computed(newMsg) console.log(newMsg.value) // 报错
</script>
上述代码在执行的时候,之所以报错,是因为 Vue3 的 computed 函数中没有 this,直接通过 computed 函数去执行 mapState 和 mapGetters 返回的结果 msg 和 newMsg 以上两个方法会报错,报错内容分别为:
报错! Cannot read properties of undefined (reading 'state') at ReactiveEffect.mappedState [as fn]
报错! Cannot read properties of undefined (reading 'getters') at ReactiveEffect.mappedGetter [as fn]
原因就是因为 Vue3 的 computed 函数里没有 this,导致最后在 "msg.value" 获取值时 mappedState 函数里的 this.$store.state 是在一个 undefined 身上去读取属性,所以报错。mapState 和 mapGetters 的报错原因一致。
二、解决方法如下
1、mapState 方式,通过给 mapState 函数返回的结果 mappedState 函数 bind 绑定一个 {$store} 对象,使得其函数内的 this.$store = $store,而不是 undefined,而 $store = userStore(),是一个变量。
注* {$store} 是 {'$store': $store} 的简写,this.$store === {'$store': $store}.$store === $store
<script setup>import { computed } from 'vue'import { useStore, mapState } from 'vuex'const $store = useStore()// mapState 返回的 msg 是一个 mappedState 函数let { msg } = mapState(['msg']) // 计算属性返回的 msg 是一个 ComputedRefImpl 引用对象msg = computed(msg.bind({$store}))console.log(msg.value) // 'hello world'
</script>
2、magGetters
<script setup>import { computed } from 'vue'import { useStore, mapGetters } from 'vuex'const $store = useStore()// mapGetters 返回的 newMsg 是一个 mappedGetter 函数let { newMsg } = mapGetters (['newMsg']) // 计算属性返回的 newMsg 是一个 ComputedRefImpl 引用对象newMsg = computed(newMsg.bind({$store}))console.log(newMsg.value) // 'Hi~ hello world'
</script>
但是每次使用 mapState 和 mapGetters 都要去绑定一个 store 对象,这很不方便,所以,自己改造封装一个 mapState 和 mapGetters,名字改成大写的 MAPSTAGE 和 MAPGETTERS。
开始封装,准备一个脚本文件
store/mapStoreReset.js,除了全局模式,还兼容 modules 模块的获取方式。
import { computed } from 'vue'
import { useStore, mapState, mapGetters } from 'vuex'
// mapActions, mapMutations 组件中可以直接使用原生写法,不需要封装/*** 重新封装 mapState,使用方法与返回结果跟官方原生 mapState 一致,适用全局和 modules 局部* 推荐使用数组形式获取,写法更简洁*/
const MAPSTATE = (...args) => {const $store = useStore()const methodName = 'MAPSTATE'const target = 'state'let modulesName = nulllet params = nulllet storeState = {}// 检测参数在 state 中是否存在const paramsCheckFn = (paramsValusArgs, $storeState) => {paramsValusArgs.forEach(item => {if (!Object.keys($storeState).includes(item)) {throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`)}})}// 调用 mapState 的参数为对象const useObjectTypeFn = (params, $storeState) => {if (Object.prototype.toString.call(params) === '[object Object]') {const paramsValusArgs = Object.values(params)paramsCheckFn(paramsValusArgs, $storeState)}}// 调用 mapState 的参数为数组const useArrayTypeFn = (params, $storeState) => {if (Array.isArray(params)) {if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0')paramsCheckFn(params, $storeState)}}// computed 优化,给 mapState 返回的每个函数的 this 绑定 {$store} 对象const improveComputedFn = storeStateFnsObj => {Object.keys(storeStateFnsObj).forEach(key => {const fn = storeStateFnsObj[key].bind({$store})storeState[key] = computed(fn)})}if (args && args.length === 1) {// 用户调用全局 Vuexparams = args[0]useObjectTypeFn(params, $store.state)useArrayTypeFn(params, $store.state)improveComputedFn(mapState(params))} else if (args && args.length === 2) {// 用户调用局部模块化 VuexmodulesName = args[0]params = args[1]useObjectTypeFn(params, $store.state[modulesName])useArrayTypeFn(params, $store.state[modulesName])improveComputedFn(mapState(modulesName, params))}return storeState
}/*** 重新封装 mapGetters,使用方法与返回结果跟官方原生 mapGetters 一致,适用全局和 modules 局部* 推荐使用数组形式获取,写法更简洁*/
const MAPGETTERS = (...args) => {const $store = useStore()const methodName = 'MAPGETTERS'const target = 'getters'let modulesName = nulllet params = nullconst storeGetters = {}// 检测参数在 getters 中是否存在const paramsCheckFn = (paramsValusArgs, isModulesName) => {// 检测参数在 getters 中是否存在paramsValusArgs.forEach(item => {if (isModulesName) {item = `${modulesName}/${item}`} if (!Object.keys($store.getters).includes(item)) {throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`)}})}// 调用 mapGetters 的参数为对象const useObjectTypeFn = (params, modulesName) => {if (Object.prototype.toString.call(params) === '[object Object]') {const paramsValusArgs = Object.values(params)paramsCheckFn(paramsValusArgs, modulesName)}}// 调用 mapGetters 的参数为数组const useArrayTypeFn = (params, modulesName) => {if (Array.isArray(params)) {if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0')paramsCheckFn(params, modulesName)}}// computed 优化,给 mapGetters 返回的每个函数的 this 绑定 {$store} 对象const improveComputedFn = storeGettersFnsObj => {Object.keys(storeGettersFnsObj).forEach(key => {const fn = storeGettersFnsObj[key].bind({$store})storeGetters[key] = computed(fn)})}if (args && args.length === 1) {// 用户调用全局 Vuexparams = args[0]useObjectTypeFn(params)useArrayTypeFn(params)improveComputedFn(mapGetters(params))} else if (args && args.length === 2) {// 用户调用局部模块化 VuexmodulesName = args[0]params = args[1]useObjectTypeFn(params, modulesName)useArrayTypeFn(params, modulesName)improveComputedFn(mapGetters(modulesName, params))}return storeGetters
}export { MAPSTATE, MAPGETTERS }
封装完成,现在来个例子,往下看。
新建一个模块文件 store/modules/hobby.js
export default {namespaced: true, // 模块化时必须要将这个属性设置为 truestate = {hobby: '不抽烟',plan: '不喝酒',like: '不烫头'},getters: {totalHobby: state => `2023 流行 ${state.hobby}-${state.plan}-${state.like}`}
}
在最新的 store/index.js 文件中引入上面的 modules 模块文件 hobby.js。
import { createStore } from 'vuex'
import hobby from './modules/hobby' // 导入文件
const state = {msg: 'hello world'
}const getters = {newMsg: state => `Hi~ ${state.msg}`
}const modules = {hobby // 在模块中使用
}export default createStore({state, getters, modules
})
Vue 组件使用
<script setup>// 导入封装好的方法import { MAPSTATE, MAPGETTERS } from '@/store/mapStoreReset'// 获取 state 数据// 使用方式1 :对象形式const { msg } = MAPSTATE({msg: 'msg'})// 使用方式2 :数组形式const { msg } = MAPSTATE(['msg'])// 以上两种方式获取的是全局的 state 对象里的数据,结果为 // msg = 'hello world'// 使用方式3 :对象形式-获取 modules 模块里的 hobby.js 的 state 数据const { hobby, plan, like } = MAPSTATE('hobby', {hobby: 'hobby', plan: 'plan', like: 'like'})// 使用方式4 :数组形式-获取 modules 模块里的 hobby.js 的 state 数据const { hobby, plan, like } = MAPSTATE('hobby', ['hobby', 'plan', 'like'])// 以上两种方式获取的是 hobby.js 模块(局部)的 state 对象里的数据,结果为 // bobby = '不抽烟' , plan = '不喝酒' , like = '不烫头' // 获取 getters 数据// 获取方式1:对象形式const { newMsg } = MAPGETTERS({newMsg: 'newMsg'})// 获取方式2:数组形式const { newMsg } = MAPGETTERS({['newMsg'])// 以上两种方式获取的是全局的 getters 对象里的数据,结果为 // newMsg = 'Hi~ hello world'// 获取方式3:对象形式-获取 hobby.js 模块里的 getters 数据const { totalHobby } = MAPGETTERS('hobby', {totalHobby: 'totalHobby'})// 获取方式4:数组形式-获取 hobby.js 模块里的 getters 数据const { totalHobby } = MAPGETTERS({'hobby', ['totalHobby'])// 以上两种方式获取的是全局的 getters 对象里的数据,结果为 // 20223 流行 不抽烟-不喝酒-不烫头
</script>
本文属个人观点,纯手打,如有错误,欢迎指正,谢谢,完!
Vue3 直接使用Vuex的mapState和mapGetters时报错的分析及解决方案相关推荐
- vue3.0下如何使用mapState,mapGetters和mapActions
vue3.0下如何使用mapState,mapGetters和mapActions 1.新建useMapper.js 2.新建useState.js 3.新建useGetters.js 4.新建use ...
- Vuex实践-mapState和mapGetters
一.前言 本文章是vuex系列的最后一篇,主要总结的是如何使用mapState和mapGetters访问vuex中的state和getters. 二.多个模块中mapState和mapGetters的 ...
- vuex进阶 mapState、mapGetters、mapMutations、mapActions
一.state 1.1 引入vuex 以后,我们需要在state中定义变量,类似于vue中的data,通过state来存放状态 import Vue from 'vue' import Vuex fr ...
- vuex的mapState,mapGetters,mapActions,mapMutations与模块化
mapState 和mapGetters 用于生成计算属性computed mapState({"计算属性名":"State数据","xxx" ...
- [vue3+ts]方法一:使用vuex的mapState和mapGetters
src下新建hooks/useState.js import { computed } from 'vue' import { mapState, useStore } from 'vuex' // ...
- Vuex中mapState和mapGetters的区别
...mapState辅助函数 当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余.为了解决这个问题,我们可以使用...mapState辅助函数帮助我们生成计算属性,当映射的 ...
- vuex:弄懂mapState、mapGetters、mapMutations、mapActions
转载地址:https://zhuanlan.zhihu.com/p/100941659 vuex进阶 一.state 1.1 引入vuex 以后,我们需要在state中定义变量,类似于vue中的dat ...
- VUE学习(二十一)、Vuex(getters、mapState与mapGetters、mapMutations与mapActions、多组件共享数据、模块化编码)
VUE学习(二十一).Vuex(getters.mapState与mapGetters.mapMutations与mapActions.多组件共享数据.模块化编码) 一.Vuex普通实现求和案例 演示 ...
- vuex的辅助函数:mapState和mapGetters
文章目录 自己写计算属性 mapState/mapGetters生成计算属性 数组写法 对象写法 对象写法(key-value,value是一个字符串) 对象写法(key-value,value是一个 ...
最新文章
- Android快速开发系列 10个常用工具类
- 让使用SQLite的.NET应用自适应32位/64位系统
- LeetCode—207. 课程表
- java中对象作为参数_java中对象引用,特别作为参数时候注意事项
- 同步助手iphone4_88 元淘来的 iPhone 4 降级到 iOS 6,甚至还能跑 “大型游戏”
- matex2推送鸿蒙系统,拜拜了,Powered by Android!
- WebRTC下载及编译(二)
- 不同平台安装python的方式一样吗_Python软件的正确安装方式
- Linux网络抓包工具tcpdump
- 高校学籍管理系统(SQL Server数据库课程设计)
- 思维导图编辑最常用的几种Edraw Max(亿图)快捷键
- C#解决串口数据丢失问题
- 深入理解java虚拟机(zz)
- python 调用scp命令 实践
- 20-《电子入门趣谈》第四章_自己制作电路板-4.1面包板的介绍和经典案例使用教程
- 云计算入门必备的60条术语
- [Windows实用软件推荐:1]本地搜索工具Everything
- 徐无忌深入浅出网络笔记:什么是OSI七层网络模型
- 啊哈 算法 Java_《啊哈!算法》.啊哈磊.高清版.pdf
- C文件中的中文乱码 notepad2
热门文章
- 35岁前要上的33堂理财课
- python随机数random.sample
- MetaRim这个项目怎么样?机甲激战为何称为链游崛起之星
- 设计模式----原型模式
- python 面向对象之封装与类与对象
- 基于jsp(java)高校智能排课系统设计
- springboot+高校教室排课系统 毕业设计-附源码221556
- Like 运算符中出错: 字符串模式“%XXXX*XXXX%”无效
- 网络安全学习篇26_阶段一小结篇_kali中间人渗透
- Windows 10设置指纹(Windows Hello)弹窗闪退修复