【手写 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)
视图概念 视图是一个虚拟表,称其为虚拟表的原因是:视图内的数据并不属于视图本身,而属于创建视图时用到的基本表。可以认为,视图是一个表中的数据经过某种筛选后的显示方式;或者多个表中的数据经过连接筛选后的显示方…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...