【手写 Vuex 源码】第十篇 - Vuex 命名空间的实现

news2024/9/21 16:21:42

一,前言

上一篇,主要介绍了 Vuex 响应式数据和缓存的实现,主要涉及以下几个点:

  • Vuex 的响应式实现原理;
  • 响应式核心方法 resetStoreVM;
  • commit 和 dispatch 的处理;

本篇,继续介绍 Vuex-namespaced 命名空间的实现;


二,前文梳理

之前几篇,完成了 Vuex 的模块收集、模块安装、响应式数据和缓存;

  • 模块收集:通过 ModuleCollection 类,对 options 选项进行处理,构建“模块树”;
  • 模块安装:处理“模块树”,将各模块中 mutation、action、getter,注册到 store 实例上:_actions、_mutations、_wrappedGetters;将各模块的 State 状态通过 Vue.set 注册到根状态;
  • 响应式数据和缓存:通过resetStoreVM在 store 实例中创建一个 Vue 实例, 借助 Vue 能力将 state、getter 通过 data、computed 进行定义,实现数据的响应式和缓存;

接下来,继续实现 namespaced 命名空间功能;


三,命名空间的使用

1,命名空间介绍

前面已经简单介绍过:在子模块对象中添加 namespaced:true,为模块开启命名空间功能;

开启命名空间功能,相当于为每个模块添加独立的作用域,实现模块间状态和事件的隔离;

2,命名空间的使用

使用官方的 Vuex 插件,以“根模块->模块 A->模块 C”的父子模块关系为例,测试多种使用场景:

1)模块 A 和模块 C 都启用了 namespaced 命名空间:

此时,模块 C 的 changeNum:moduleA/moduleC/changeNum;

AC

2)模块 A 启用了命名空间,但模块 C 没启用命名空间:

此时,模块 C 的 changeNum:moduleA/changeNum;

A

3)模块 C 启用了命名空间,但模块 A 没启用命名空间:

此时,模块 C 的 changeNum:moduleC/changeNum;

C


三,命名空间的实现

1,命名空间的逻辑分析

根据测试情况分析命名空间的划分规则:

在模块注册阶段,会通过类似发布订阅的方式将各模块中的 action、mutation 进行手机并注册,需要根据模块是否开启命名空间状态,为模块拼接命名空间前缀;

所以,可以统一理解为,在事件订阅时,为事件添加对应命名空间标识即可;


2,命名空间的代码实现

在 installModule 模块安装阶段,获取当前模块的命名空间:

// src/vuex/store.js#installModule

/**
 * 安装模块
 * @param {*} store       容器
 * @param {*} rootState   根状态
 * @param {*} path        所有路径
 * @param {*} module      格式化后的模块对象
 */
const installModule = (store, rootState, path, module) => {
  // 根据当前模块的 path 路径,拼接当前模块的命名空间标识
  let namespace = store._modules.getNamespaced(path);
}

为 ModuleCollection 类添加 getNamespaced 方法:

// src/vuex/module/module-collection.js

class ModuleCollection {
  constructor(options) {
    this.register([], options);
  }
  /**
   * 根据当前模块的 path 路径,拼接当前模块的命名空间标识
   * @param {*} path 
   * @returns 
   */
  getNamespaced(path) {
    let root = this.root;
    // 从根模块开始,逐层处理子模块,拼接命名空间标识
    return path.reduce((str, key) => {
      // 从根模块查找当前子模块
      root = root.getChild(key);
      // 若子模块启用命名空间,拼接命名空间标识并返回结果继续处理
      return str + (root.namespaced ? key + '/' : '')
    }, '');
  }
}

测试getNamespaced获取命名空间:

模块 A、B 都开启命名空间的情况:

模块 A 不开启命名空间,模块 C 开启命名空间的情况:

结论与官方 Vuex 插件相同;

拿到了命名空间后,就需要在注册事件时,为其添加对应的命名空间标识;

在模块安装注册时,为事件添加命名空间标识:

// src/vuex/store.js#installModule

/**
 * 安装模块
 * @param {*} store       容器
 * @param {*} rootState   根状态
 * @param {*} path        所有路径
 * @param {*} module      格式化后的模块对象
 */
const installModule = (store, rootState, path, module) => {

  // 根据当前模块的 path 路径,拼接当前模块的命名空间标识
  let namespace = store._modules.getNamespaced(path);

  if (path.length > 0) {
    let parent = path.slice(0, -1).reduce((memo, current) => {
      return memo[current]
    }, rootState)
    Vue.set(parent, path[path.length - 1], module.state);
  }

  // 遍历 mutation
  module.forEachMutation((mutation, key) => {
		// 添加命名空间标识: namespace + key
    store._mutations[namespace + key] = (store._mutations[namespace + key] || []);
    store._mutations[namespace + key].push((payload) => {
      mutation.call(store, module.state, payload);
    })
  })
  // 遍历 action
  module.forEachAction((action, key) => {
    // 添加命名空间标识: namespace + key
    store._actions[namespace + key] = (store._actions[namespace + key] || []);
    store._actions[namespace + key].push((payload) => {
      action.call(store, store, payload);
    })
  })
  // 遍历 getter
  module.forEachGetter((getter, key) => {
    // 添加命名空间标识: namespace + key
    store._wrappedGetters[namespace + key] = function () {
      return getter(module.state);
    }
  })
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child);
  })
}

3,命名空间效果测试

测试示例:

<template>
  <div id="app">
    <br> 根模块测试: <br>
    商品数量: {{this.$store.state.num}} 个<br>
    商品单价: 10 元<br>
    订单金额: {{this.$store.getters.getPrice}} 元<br>
    <button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>
    <button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>

    <br> 子模块测试: <br>
    A 模块-商品数量: {{this.$store.state.moduleA.num}} 个<br>
    B 模块-商品数量: {{this.$store.state.moduleB.num}} 个<br>
    C 模块-商品数量: {{this.$store.state.moduleA.moduleC.num}} 个<br>
    <button @click="$store.commit('moduleA/changeNum',5)">A 模块-同步更新:数量+5</button>
    <button @click="$store.commit('moduleB/changeNum',5)">B 模块-同步更新:数量+5</button>
    <button @click="$store.commit('moduleA/moduleC/changeNum',5)">C 模块-同步更新:数量+5</button>
  </div>
</template>

点击按钮前(页面初始化完成)

点击按钮后(更新完成)

  • 上边区域对根模块的测试:

点击同步/异步更新按钮,通过$store.commit('changeNum',5)$store.dispatch('changeNum',-5) 操作,只能够影响到根模块中 mutation、action 对应的 changeNum 事件,不再对子模块产生影响;

  • 下边区域对子模块的测试:

点击同步更新按钮,通过$store.commit('moduleA/changeNum',5) 操作,会根据命名空间标识查找对应模块下的changeNum事件;

这样,就实现了不同子模块间状态和事件操作的隔离;


四,核心逻辑梳理

  • 在 ModuleCollection 模块收集类中,提供根据 path 获取命名空间标识的能力:getNamespaced(path)
  • 在 installModule 模块安装时,通过调用getNamespaced(path) 获取当前模块的命名空间标识;
  • 在安装/注册mutationactiongetter 时,为对应的事件添加(拼接)上命名空间标识;

这样,就实现了 Vuex 命名空间 namespaced 功能,即:根模块与各子模块中定义的事件完全独立互不影响;


五,结尾

本篇,主要介绍了 Vuex-namespaced 命名空间的实现,主要涉及以下几个点:

  • 命名空间的介绍和使用;
  • 命名空间的逻辑分析与代码实现;
  • 命名空间核心流程梳理;

下一篇,继续介绍 Vuex 插件的实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341755.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jdk-concurrentHashMap(1.8)源码学习

上文&#xff1a;jdk-HashMap(1.8)源码学习concurrentHashMap介绍concurrentHashMap是一个高性能、线程安全的HashMap&#xff0c;底层数据结构主要还是以数组链表红黑树实现与HashMap的结构是一致的。concurrentHashMap1.7和1.8的区别&#xff1f;对比名称1.71.8备注线程安全是…

PhysioNet2017分类的代码实现

PhysioNet2017数据集介绍可参考文章&#xff1a;https://wendy.blog.csdn.net/article/details/128686196。本文主要介绍利用PhysioNet2017数据集对其进行分类的代码实现。 目录一、数据集预处理二、训练2.1 导入数据集并进行数据裁剪2.2 划分训练集、验证集和测试集2.3 设置训…

C语言(C语言结构基础使用)

目录 一.结构 1.结构声明 2.初始化结构 3.访问结构成员 4.结构的初始化器 5.定义无结构标记 6.结构数组 7.嵌套结构 8.复合字面量和结构&#xff08;C99&#xff09; 9.伸缩性数组成员 10.伸缩性数组得特殊处理请求 11.匿名结构&#xff08;C11&#xff09; 12.使用结构数组得函…

RiProRiProV2主题美化顶部增加一行导航header导航通知

背景: 有些网站的背景顶部有一行罪行公告,样式不错,希望自己的网站也借鉴过来,本教程将指导如何操作,并调整成自己想要的样式。 比如网友搭的666资源站 xd素材中文网

【C语言必经之路——第11节】初阶指针(2)

五、指针的运算1、指针与整数相加减看一下下面的代码&#xff1a;#include<stdio.h> int my_strlen(char* str) {int count0;while(*str!\0){count;str;//指针加减整数}return count; } int main() {int lenmy_strlen("abcdef");printf("%d\n",len);…

OpenCV实战(10)——积分图像详解

OpenCV实战&#xff08;10&#xff09;——积分图像详解0. 前言1. 积分图像计算2. 自适应阈值2.1 固定阈值的缺陷2.2 使用自适应阈值2.3 其它自适应阈值计算方法2.4 完整代码3. 使用直方图进行视觉跟踪3.1 查找目标对象3.2 完整代码小结系列链接0. 前言 我们知道直方图是通过遍…

方法递归调用

&#x1f3e1;个人主页 &#xff1a; 守夜人st &#x1f680;系列专栏&#xff1a;Java …持续更新中敬请关注… &#x1f649;博主简介&#xff1a;软件工程专业&#xff0c;在校学生&#xff0c;写博客是为了总结回顾一些所学知识点 ✈️推荐一款模拟面试&#xff0c;刷题神器…

【C++设计模式】学习笔记(4):观察者模式 Observer

目录 简介动机(Motivation)模式定义结构(Structure)要点总结笔记结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金…

渣土车智能识别检测 yolov5

渣土车智能识别检测通过yolov5网络模型深度学习技术&#xff0c;渣土车智能识别检测对禁止渣土车通行现场画面中含有渣土车时进行自动识别监测&#xff0c;并自动抓拍告警。YOLOv5是一种单阶段目标检测算法&#xff0c;该算法在YOLOv4的基础上添加了一些新的改进思路&#xff0…

【Redis场景3】缓存穿透、击穿问题

场景问题及原因 缓存穿透&#xff1a; 原因&#xff1a;客户端请求的数据在缓存和数据库中不存在&#xff0c;这样缓存永远不会生效&#xff0c;请求全部打入数据库&#xff0c;造成数据库连接异常。 解决思路&#xff1a; 缓存空对象 对于不存在的数据也在Redis建立缓存&a…

spark01-内存数据分区数量个数原理

原始代码如下&#xff1a;val conf: SparkConf new SparkConf().setMaster("local[*]").setAppName("wordcount")val scnew SparkContext(conf)val rdd: RDD[Int] sc.makeRDD(List(1,2,3,4)//将处理的数据保存分区文件rdd.saveAsTextFile("output2&…

分布式数据库(ShardingSphere)

单库单表数据量过大导致的问题与应对传统的将数据集中存储至单一数据节点的解决方案&#xff0c;在容量、性能、可用性和运维成本这三方面已经难于满足互联网的海量数据场景。我们在单库单表数据量超过一定容量水位的情况下&#xff0c;索引树层级增加&#xff0c;磁盘 IO 也很…

数据库(六): MySQL的主从复制和读写分离

文章目录一、为什么要使用主从复制和读写分离二、主从复制的原理三、如何实现主从复制3.1 master配置3.2 slave配置3.3 测试主从复制四、读写分离五、缺点一、为什么要使用主从复制和读写分离 注意到主从复制和读写分离一般是一起使用的。目的很简单&#xff0c;就是提高数据库…

Python:路径之谜(DFS剪枝)

题目描述 小张冒充 X 星球的骑士&#xff0c;进入了一个奇怪的城堡。 城堡里边什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡地面是 nn 个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或纵向移动&#xff0c;但不能斜着走&#xf…

Java类和对象超详细整理,适合新手入门

目录 一、驼峰命名法 二、Java注释 三、转义符 四、Java程序它的基本结构是什么&#xff1f; 五、Java中的类 六、创建类 七、定义main方法 八、执行代码输出语句 九、Java中的对象 十、创建对象 十一、类与对象的关系 一、驼峰命名法 包名&#xff1a;多单词组成所…

常用类详解(二)StringBuffer

StringBuffer类&#xff1a; 基本介绍&#xff1a; java.lang.StringBuffer代表可变的字符序列&#xff0c;可以对字符串内容进行增删 很多方法与String相同&#xff0c;但StringBuffer是可变长度的。 StringBuffer是一个容器。 我们进行查看StringBuffer&#xff0c;如下…

fpga设计中如何防止信号被优化

本文分别对quartus和vivado防止信号被优化的方法进行介绍。 为什么要防止信号被优化 ​ 在FPGA开发调试阶段&#xff0c;经常遇到这样的情况&#xff0c;需要临时添加信号&#xff0c;观察信号变化&#xff0c;用来定位代码中存在的问题&#xff0c;很多时候这些临时添加的信…

sg3_utils arm64 静态编译

需求背景 在进行ufs等scsi device测试时&#xff0c;需要进行power mode切换等测试&#xff0c;因此需要有一个简单地工具集来向scsi device&#xff08;ufs接口&#xff09;发送scsi命令&#xff0c;比如 scsi reset命令等。在网上调研后发现sg3_utils是一个比较全面的工具。…

本地代码提交至gitee仓库

1、新建仓库 新建一个私人访问的仓库 2、创建公钥 点开cmd 输入ssh-keygen -t rsa -C "xxxxxxxxxx.com" 邮箱填入自己使用的即可。 输入完毕后&#xff0c;连按三次enter。 命令就会执行完毕&#xff0c;会出现这个界面 此时已经代表ssh公钥已经创建完毕。 公…

自动驾驶TPM技术杂谈 ———— 摄像头标定

文章目录介绍摄像头内参标定摄像头模型的建立摄像头坐标系与环境坐标系的转换图像坐标系与图像像素坐标系小孔成像与图像物理坐标系环境坐标系与图像像素坐标系的转换摄像头畸变矫正常见内参标定方法平面标定自标定摄像头间外参标定介绍 标定传感器是自动驾驶感知系统中不可缺少…