【手写 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)
视图概念 视图是一个虚拟表,称其为虚拟表的原因是:视图内的数据并不属于视图本身,而属于创建视图时用到的基本表。可以认为,视图是一个表中的数据经过某种筛选后的显示方式;或者多个表中的数据经过连接筛选后的显示方…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...