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)

相关推荐

  • 详解vuex中mapState,mapGetters,mapMutations,mapActions的作用

    在开始接触vuex框架的时候对那些state,action,mutation,getter等理解的还挺顺利的,然后突然出来一种加了一个前缀的mapState等,这样的就有点蒙圈了...特别是官方的文档并没有给除详细的说明跟例子...然后就自己慢慢理解了一下.其实也就是一个重命名而已...以下就是例子,希望能帮助理解: 在store中代码 import Vuex from 'vuex' import Vue from 'vue' Vue.use(Vuex); const store = new V

  • 详解Vuex中mapState的具体用法

    今天使用Vuex的时候遇到一个坑,也可以说是自己的无知吧,折腾了好久,终于发现自己代码的错误了.真是天雷滚滚~~~~~~ index.js import Vue from 'vue' import Vuex from 'vuex' import mutations from './mutations' import actions from './action' import getters from './getters' Vue.use(Vuex) const state = { userI

  • VUE中的mapState和mapActions的使用详解

    最近在开发一套系统,前端使用VUE开发,由于本人是后端开发,前端也会一点,但是VUE接触不多,在VUE项目开发遇到的一些坑记录一下,不是专业前端写好的不好,大家不要唝... 在VUE项目中经常会用到mapState和mapActions,mapState主要用于同步全局的变量或者对象,mapActions主要是用于同步定义的方法,一般两者是结合使用,mapState同步项目中定义的全局的变量或者对象,mapActions是用于变量或者对象为空时,调用方法定义的全局方法获取. mapActions

  • 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的方式 // 使用m

  • Vue3.0 axios跨域请求代理服务器配置方式

    目录 axios跨域请求代理服务器配置 axios跨域问题解决 1.在vue.config.js文件中做如下配置 2.request.js(拦截器页面)如下配置 3.具体请求页面如下配置 axios跨域请求代理服务器配置 首先安装axios npm install axios 然后在vue.config.js文件下配置如下代码 (若没有vue.config.js文件可以自己创建这个文件) module.exports = { devServer: { open: true, port: 8001

  • vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用

    一.介绍 vuex里面的四大金刚:State, Mutations,Actions,Getters (上次记得关于vuex笔记 http://www.jb51.net/article/138229.htm,是一个简单的应用:这是一些简单的vue的小组件方法: http://www.jb51.net/article/138230.htm) 何为四大金刚? 1.State (这里可以是 小写的 state,跟官网保持一致,采用大写,因为个人习惯,后面的代码介绍采用小写) vuex的状态管理,需要依赖

  • Vue3.0中的monorepo管理模式的实现

    前言 前段时间9月21日参加了在成都举办的第五届FEDAY, 印象比较深刻的是白鹭引擎首席架构师@王泽分享的<框架开发中的基础设施搭建>,提到了在下一代白鹭引擎中使用到monorepo模式,以用来管理多个模块,协调各个模块之间的依赖更新. 正好在国庆期间10月5日尤大公开了vue3.0已完成的源码,也是采用了monorepo管理模式,看来monorepo确实有其独到的优势,再加上以前在项目中遇到过相关的痛点,所以深入地了解了一下这种模式,本文将基于vue3.0讨论如何通过monorepo模式来

  • vue3.0 CLI - 3.2 路由的初级使用教程

    我的 github 地址 -vue3.0Study- 阶段学习成果都会建立分支. ========================== 动态路由 在路由某部分里加入[ : ],就成为动态路由如:/user/:id/,那么路由导航,并不是  /user/id/ 而是 /user/666/. 显然这个 id 能被获取,在组件中使用.通过 this.$route.params 获取. this 是当前组件,$route 是路由对象,params 是一个对象字面量 { id:666 }. $route

  • vue3.0 CLI - 2.2 - 组件 home.vue 的初步改造

    我的 github 地址 - vue3.0Study- 阶段学习成果都会建立分支. helloworld.vue 都挪到 about 路由当中. <template><div class="about"><HelloWorld msg="vue 官方相关资料的链接"/></div></template> <script> import HelloWorld from '@/components/

  • vue3.0 CLI - 2.1 - component 组件入门教程

    我的 github 地址 - vue3.0Study - 阶段学习成果都会建立分支. 进入 src 文件夹,这是实际都工程文件夹,其他文件夹以及文件以后在了解. 3个文件夹  assets - 各类静态资源文件夹 - 比如 图片, css 文件等.  components - 组件文件夹, 组件是 vue 等 MVC 框架等核心概念,自行了解含义. view - 视图文件夹. 5个文件  app.vue.main.js - 主视图.配合 main.js 成为 vue 程序的主入口.router.

  • 初探Vue3.0 中的一大亮点Proxy的使用

    前言 不久前,也就是11月14日-16日于多伦多举办的 VueConf TO 2018 大会上,尤雨溪发表了名为 Vue3.0 Updates 的主题演讲,对 Vue3.0 的更新计划.方向进行了详细阐述,表示已经放弃使用了 Object.defineProperty,而选择了使用更快的原生 Proxy !! 这将会消除了之前 Vue2.x 中基于 Object.defineProperty 的实现所存在的很多限制:无法监听 属性的添加和删除.数组索引和长度的变更,并可以支持 Map.Set.W

  • Vue3.0结合bootstrap创建多页面应用

    本人主要做c#winform应用,刚接触vue,发现用vue做单页面应用的比较多,多页面的资料很少,特别是用vue3.0版本做多页面的资料,更少,所以自己整理一下,结合bootstrap和jquery,也可以给有需要的同行做个参考. 1.首先用 vue --version 命令查一下安装的vue-cli版本,要3.x版本,我原先装的是2.9.6版本的 2.卸载vue-cli,执行命令:cnpm uninstall -g vue-clinpm uninstall -g vue-cli ,执行完就好

随机推荐