Open jtwang7 opened 2 years ago
参考文章:
🔆 问题场景:在 Vue 中访问 Vuex 的状态 state 和计算属性 getters 时,为了保留取出变量的响应性,需要用 computed 包裹为计算属性。在 Vue 的与组件实例绑定的 computed 属性中,官方提供了一系列配套的组建绑定的辅助函数 ,例如 mapState, mapGetters, mapMutations 等帮助开发者更方便的遍历包裹状态变量,保留响应性并添加到 Vue 中。但是在 Vue3 的 setup 中,单独使用 mapState 等辅助函数无效(原因见下),因此需要做二次封装。
❎ 简单的解决方案(不推荐) - 直接用 computed
import {computed} from "vue" import {mapState, useStore} from "vuex" export default { setup() { const store = useStore() const counter = computed(() => store.state.counter) return { counter } } }
直接用 computed ,会写出很多重复性的代码
const counter = computed(() => store.state.counter) const name = computed(() => store.state.name) const age= computed(() => store.state.age) const counter = computed(() => store.state.counter) const counter = computed(() => store.state.counter)
✅ mapState + computed = useState mapState 实际是将 store 内 state 的各项属性值用 computed 包裹,保留其响应性,然后以对象的形式抛出,最终输出结果如下:
mapState
/** * mapState返回的数据结构: * { * name: function(){return 'xxx'}, * age: function(){return 'xxx'} * } */
mapState 这样组织数据的原因是因为要匹配 Vue 内与组件实例绑定的 computed 属性字段的映射格式 (接收一个对象,其键值为一个函数),参考官方文档 computed。
知道了 mapState 等辅助函数的作用原理及返回的数据格式,我们就可以自己封装一个函数,该函数的目的就是将 mapState 等辅助函数返回的值重新用 computed() 再次封装。
computed()
🔆 值得注意的是:mapState 等一系列辅助函数在解析 state 或者 getters 等数据时,是需要通过 this.$store 去解析的,这个在组件实例中不需要考虑,因为函数会自动引用组件实例的 this,但是在 setup 中,由于没有 this ,所以我们给它的函数单独绑定 ctx。
this.$store
import {mapState, useStore} from "vuex" import {computed} from "vue" export default { setup() { const store = useStore() const storeStateFns = mapState(['name', 'age', 'gender']) /** * mapState返回的数据结构: * { * name: function(){return 'xxx'}, * age: function(){return 'xxx'} * } */ const storeState = {} Object.keys(storeStateFns).forEach(fnKey => { // mapState在解析state的数据时,是需要通过this.$store去解析 // 在setup里面是没有this的,所以我们给它的函数绑定ctx // this => {$store: store} const fn = storeStateFns[fnKey].bind({$store: store}) // 遍历生成这种数据结构 => {name: ref(), age: ref()} storeState[fnKey] = computed(fn) }) return { ...storeState } } }
✅ 封装为 hook 方便使用
import { computed } from "vue" import { mapState, useStore } from "vuex" export default function (state) { // 1. 获取实例 $store const store = useStore() // 2. 遍历状态数据 const storeStateFns = mapState(state) // 3. 存放处理好的数据对象 const storeState = {} // 4. 对每个函数进行computed Object.keys(storeStateFns).forEach(fnKey => { const fn = storeStateFns[fnKey].bind({ $store: store }) // 遍历生成这种数据结构 => {name: ref(), age: ref()} storeState[fnKey] = computed(fn) }) return storeState }
✅ 实例
import { computed, ComputedRef } from "vue"; import { mapState, mapGetters, mapMutations, MutationMethod, } from "vuex"; import _ from "lodash"; import store from "@/store"; export type StoreStateMap = Record<string, ComputedRef<any>>; export type StoreMutationMap = Record<string, MutationMethod>; export function useState(map: any): StoreStateMap; export function useState(namespace: string, map: any): StoreStateMap; export function useState(namespace?: string, map?: any): StoreStateMap { let state = null; if (namespace) { state = mapState(namespace, map); } else { state = mapState(map); } // 借助 lodash _.mapValues() 简化操作 const computedState: StoreStateMap = _.mapValues(state, (computedFunc) => computed(computedFunc.bind({ $store: store })) ); return computedState; } export function useGetters(map: any): StoreStateMap; export function useGetters(namespace: string, map: any): StoreStateMap; export function useGetters(namespace?: string, map?: any): StoreStateMap { let getters = null; if (namespace) { getters = mapGetters(namespace, map); } else { getters = mapGetters(map); } const computedState: StoreStateMap = _.mapValues(getters, (computedFunc) => computed(computedFunc.bind({ $store: store })) ); return computedState; } export function useMutations(map: any): StoreMutationMap; export function useMutations(namespace: string, map: any): StoreMutationMap; export function useMutations(namespace?: string, map?: any): StoreMutationMap { let mutations = null; if (namespace) { mutations = mapMutations(namespace, map); } else { mutations = mapMutations(map); } const wrappedMutations: StoreMutationMap = _.mapValues(mutations, (func) => // mutations 等不需要包裹为 computed 响应式,直接绑定 store 即可 func.bind({ $store: store }) ); return wrappedMutations; }
Vue3 setup 中 mapState 和 computed 结合的正确“食用”方式
参考文章:
🔆 问题场景:在 Vue 中访问 Vuex 的状态 state 和计算属性 getters 时,为了保留取出变量的响应性,需要用 computed 包裹为计算属性。在 Vue 的与组件实例绑定的 computed 属性中,官方提供了一系列配套的组建绑定的辅助函数 ,例如 mapState, mapGetters, mapMutations 等帮助开发者更方便的遍历包裹状态变量,保留响应性并添加到 Vue 中。但是在 Vue3 的 setup 中,单独使用 mapState 等辅助函数无效(原因见下),因此需要做二次封装。
❎ 简单的解决方案(不推荐) - 直接用 computed
✅ mapState + computed = useState
mapState
实际是将 store 内 state 的各项属性值用 computed 包裹,保留其响应性,然后以对象的形式抛出,最终输出结果如下:mapState
这样组织数据的原因是因为要匹配 Vue 内与组件实例绑定的 computed 属性字段的映射格式 (接收一个对象,其键值为一个函数),参考官方文档 computed。知道了
mapState
等辅助函数的作用原理及返回的数据格式,我们就可以自己封装一个函数,该函数的目的就是将mapState
等辅助函数返回的值重新用computed()
再次封装。🔆 值得注意的是:
mapState
等一系列辅助函数在解析 state 或者 getters 等数据时,是需要通过this.$store
去解析的,这个在组件实例中不需要考虑,因为函数会自动引用组件实例的 this,但是在 setup 中,由于没有 this ,所以我们给它的函数单独绑定 ctx。✅ 封装为 hook 方便使用
✅ 实例