【手写 Vuex 源码】第十篇 - Vuex 命名空间的实现
一,前言
上一篇,主要介绍了 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;
2)模块 A 启用了命名空间,但模块 C 没启用命名空间:
此时,模块 C 的 changeNum:moduleA/changeNum;
3)模块 C 启用了命名空间,但模块 A 没启用命名空间:
此时,模块 C 的 changeNum:moduleC/changeNum;
三,命名空间的实现
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.jsclass 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);}// 遍历 mutationmodule.forEachMutation((mutation, key) => {// 添加命名空间标识: namespace + keystore._mutations[namespace + key] = (store._mutations[namespace + key] || []);store._mutations[namespace + key].push((payload) => {mutation.call(store, module.state, payload);})})// 遍历 actionmodule.forEachAction((action, key) => {// 添加命名空间标识: namespace + keystore._actions[namespace + key] = (store._actions[namespace + key] || []);store._actions[namespace + key].push((payload) => {action.call(store, store, payload);})})// 遍历 gettermodule.forEachGetter((getter, key) => {// 添加命名空间标识: namespace + keystore._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)
获取当前模块的命名空间标识; - 在安装/注册
mutation
、action
、getter
时,为对应的事件添加(拼接)上命名空间标识;
这样,就实现了 Vuex 命名空间 namespaced 功能,即:根模块与各子模块中定义的事件完全独立互不影响;
五,结尾
本篇,主要介绍了 Vuex-namespaced 命名空间的实现,主要涉及以下几个点:
- 命名空间的介绍和使用;
- 命名空间的逻辑分析与代码实现;
- 命名空间核心流程梳理;
下一篇,继续介绍 Vuex 插件的实现
相关文章:

【手写 Vuex 源码】第十篇 - Vuex 命名空间的实现
一,前言 上一篇,主要介绍了 Vuex 响应式数据和缓存的实现,主要涉及以下几个点: Vuex 的响应式实现原理;响应式核心方法 resetStoreVM;commit 和 dispatch 的处理; 本篇,继续介绍 …...

面试腾讯测试岗后感想,真的很后悔这5年一直都干的是基础测试....
前两天,我的一个朋友去大厂面试,跟我聊天时说:输的很彻底… 我问她:什么情况?她说:很后悔这5年来一直都干的是功能测试… 相信许多测试人也跟我朋友一样,从事了软件测试很多年,却依…...
知识图谱 方法、实践与应用 王昊奋 读书笔记(下)
最近读了这本书,在思路上很有启发,对知识图谱有了初步的认识,以下是原书后半部分的内容,可以购买实体书获取更多内容。 知识图谱推理 结合已有规则,推出新的事实,例如持有股份就能控制一家公司࿰…...

vue实现打印浏览器页面功能(两种方法)
推荐使用方法二 方法一:通过npm 安装插件 1,安装 npm install vue-print-nb --save 2,引入 安装好以后在main.js文件中引入 import Print from vue-print-nbVue.use(Print); //注册 3,现在就可以使用了 div id"printTest…...

【VictoriaMetrics】VictoriaMetrics单机版批量和单条数据写入(Prometheus格式)
VictoriaMetrics单机版支持以Prometheus格式的数据写入,写入支持单条数据写入以及多条数据写入,下面操作演示下如何使用 1、首先需要启动VictoriaMetrics单机版服务 2、使用postman插入单机版VictoriaMetrics,以当前时间插入数据 地址为 http://victoriaMetricsIP:8428/api…...

【青训营】分布式定时任务简述
这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天 分布式定时任务简述 定义 定时任务是指系统为了自动完成特定任务,实时、延时、周期性完成任务调度的过程。分布式定时任务是把分散的、可靠性差的定时任务纳入统一平台,并且实现集群管理调度和…...

golang语言本身设计点总结
本文参考 1.golang的内存管理分配 golang的内存分配仿造Google公司的内存分配方法TCmalloc算法;她会把将内存请求分为两类,大对象请求和小对象请求,大对象为>32K的对象。 在了解golang的内存分配之前要知道什么事虚拟内存,虚拟内存是把磁盘作为全局…...

PTA L1-046 整除光棍(详解)
前言:内容包括四大模块:题目,代码实现,大致思路,代码解读 题目: 这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被…...

将小程序代码转成uni-app代码
最近因为公司项目原因需要将小程序的项目转换成uni—app的项目,所以总结了以下几点: 首先你可以先到uni-app的官网简单看一下对它的介绍,本次文章的介绍是针对简单的微信小程序来进行的转化。 在这之前我们来看一下目录对比 下面就来介绍一下…...

C语言在游戏中播放音乐
使用 mciSendString 播放音乐 mciSendString 支持 mp3、wma、wav、mid 等多种媒体格式,使用非常简单。这里做一个简单的范例,用 mciSendString 函数播放 MP3 格式的音乐,代码如下: // 编译该范例前,请把 music.mp3 放…...

机器学习算法:随机森林
在经典机器学习中,随机森林一直是一种灵丹妙药类型的模型。 该模型很棒有几个原因: 与许多其他算法相比,需要较少的数据预处理,因此易于设置充当分类或回归模型不太容易过度拟合可以轻松计算特征重要性在本文[1]中,我想…...

如何做好多项目全生命周期的资源调配,提升资源利用效率?【橙子】
随着产品研发中心各团队承接的研发项目数量和规模日趋增加,人均产值和利润目标逐步提升,人均承接的项目数量也逐渐增加,目前缺乏合理的研发资源管理方案,存在多项目研发过程中资源冲突及部分项目研发人员忙闲不均等现象࿰…...

JVM - 内存分配
目录 JVM的简化架构和运行时数据区 JVM的简化架构 运行时数据区 PC寄存器 Java栈 Java堆 方法区 运行时常量池 本地方法栈 栈、堆、方法区交互关系 Java堆内存模型和分配 Java堆内存概述 Java堆的结构 对象的内存布局 对象的访问定位 Trace跟踪和Java堆的参数配…...

【知识图谱论文】Bi-Link:通过转换器和提示的对比学习桥接来自文本的归纳链接预测
文献题目:Bi-Link: Bridging Inductive Link Predictions from Text via Contrastive Learning of Transformers and Prompts发表期刊:WWW2023代码: https://anonymous.4open.science/r/Bi-Link-2277/. 摘要 归纳知识图的完成需要模型来理解…...

jieba+wordcloud 词云分析 202302 QCon 议题 TOP 关键词
效果图 步骤 (1)依赖 python 库 pip install jieba wordcloud数据 概览 $ head -n 5 input.txt 中国软件技术发展洞察和趋势预测报告 2023 QCon 大会内容策划思路 FinOps:从概念到落地 开源芯片的发展现状、机遇和未来 乐观者前行࿰…...

包管理工具-npm-npx-yarn-cnpm
代码共享方案 在我们通过模块化的方式将代码划分成一个个小的结构后,在以后的开发中我们就可以通过模块化的方式来封装自己的代码,并且封装成一个工具,这个工具我们可以让同事通过导入的方式来使用,甚至你可以分享给世界各地的程…...

go gin学习记录1
环境: MAC M1,Go 1.17.2,GoLand 默认执行指令的终端,如果没有特别说明,指的都是goland->Terminal 创建项目 Goland中新建项目,在$GOPATH/src/目录下建立t_gin项目。 进入项目,在goland的T…...

Docker常用命令
1:帮助命令docker versiondocker infodocker --help2:镜像命令docker images(列出本地主机上的镜像)各个选项说明:docker imagesREPOSITORY:表示镜docker images像的仓库源TAG:镜像的标签IMAGE IDÿ…...

论文写作——公式编辑器、latex表格、颜色搭配器
1、公式编辑器(网页版mathtype可用于latex公式编辑): MathType demo - For DevelopersLive demonstration about the features of Mathtype which allows edition equations and formulas (PNG, flash, SVG, PDF, EPS), based on MathML and compatible with LaTeX.https:/…...

MySQL数据库12——视图(VIEW)
视图概念 视图是一个虚拟表,称其为虚拟表的原因是:视图内的数据并不属于视图本身,而属于创建视图时用到的基本表。可以认为,视图是一个表中的数据经过某种筛选后的显示方式;或者多个表中的数据经过连接筛选后的显示方…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...