js模块化:修改导入模块的内容,会有影响吗?

news2024/11/23 21:13:56

起因

element-ui的popper组件相关的层级,是使用popup-manager来统一管理的。

之前试图在自己的组件里导入并使用element-ui的popup-manager,但是层级老是和element-ui组件的层级冲突,看了下源码,竟意外发现,使用popup-manager时,是调用其内部方法nextZIndex修改导出的PopupManager.zIndex,来实现不同popper的正确层级:

/* PopupManager */
const PopupManager = {
  nextZIndex: function() {
    return PopupManager.zIndex++;
  },
}

export default PopupManager;



/* 其他地方使用 */
import PopupManager from 'element-ui/src/utils/popup/popup-manager';

PopupManager.nextZIndex()

// 或者直接在外部修改
PopupManager.zIndex = zIndex;

PopupManager这种管理层级的方法,有点类似于“全局变量”,即只要是导入该模块的文件,实际是共享一个zIndex变量,以达到zIndex的正确累加。经过构建工具的打包之后,这些使用PopupManager.zIndex的模块,实际上是使用了一个变量。而我再导入的popup-manager,显然和element-ui使用的popup-manager不是一个对象。

但自己之前似乎从未有过这种作法,即直接修改其他模块的导出。同时不禁有个疑惑,这种做法真的有效吗,构建工具到底是怎么处理模块化的?干脆直接看下webpack打包后的代码吧。

webpack处理模块化示例

准备工作

首先,准备一个webpack项目,内容很简单,src/index.js是打包入口,src/modules目录下有三个模块a、b、c

a模块导出一个变量name

b模块中导入并尝试修改a模块导出的name

c模块导入a模块,检测a中的name有没有被修改

最后,在index.js导入b、c

打包并执行

没有什么意外,模块导出的变量,确实可以在其他模块中修改,a.name从原先的'jyj',被修改为了'b'

为啥会这样

直接看webpack打包后的代码:

核心:__webpack_require__方法,即require方法,调用该方法时,会优先从__webpack_module_cache__中返回已缓存的模块,这也提示我们,相同模块的代码,只会执行一次。

// The module cache
var __webpack_module_cache__ = {};
 	
// The require function
function __webpack_require__(moduleId) {
	// Check if module is in cache
	var cachedModule = __webpack_module_cache__[moduleId];
	if (cachedModule !== undefined) {
		return cachedModule.exports;
	}
	// Create a new module (and put it into the cache)
	var module = __webpack_module_cache__[moduleId] = {
		// no module.id needed
		// no module.loaded needed
		exports: {}
	};

	// Execute the module function
	__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

	// Return the exports of the module
	return module.exports;
}

如果引入的模块没有加载过,那么webpack会从__webpack_modules__对象中取出需要导入的模块函数,并且向该函数的上下文中传入module, module.exports, __webpack_require__三个变量,执行对应模块的代码。这三个变量对应了我们在模块中使用的module, module.exports, require方法,模块内部会使用module.exports = {}这样的语法,将要导出的内容挂载到module上。

同时可以发现,所谓的模块,就是一个函数。

var __webpack_modules__ = ({

/***/ "./src/modules/a.js": ((module) => {

        const name = 'jyj'

        console.log('a加载了');

        module.exports = {
          name
        }

      }),

/***/ "./src/modules/b.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        a.name = "b"


      }),

/***/ "./src/modules/c.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        console.log(a.name, 'a.name');


      })

});

现在我们已经理解了webpack处理模块化的基本原理。再来看入口index.js进行了哪些处理。

由于index.js引入了b、c模块,webpack调用__webpack_require__,引入这两个模块,

// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
__webpack_require__(/*! ./modules/b.js */ "./src/modules/b.js")
__webpack_require__(/*! ./modules/c.js */ "./src/modules/c.js")
})();

这个时候,b、c并未加载,所以webpack会去加载b、c,即__webpack_modules__[moduleId](module, module.exports, __webpack_require__)。

由于先导入b模块,所以先执行b.js。b又导入了a模块,同理,webpack又去加载执行a模块,加载完成后,__webpack_module_cache__中已经有了a模块导出的对象module.exports。__webpack_require__(/*! ./a.js */ "./src/modules/a.js")就是a模块的module.exports,即{name: 'jyj'}。

b.js中将name修改为'b',a模块的module.exports此时已经为{name: 'b'},

/***/ "./src/modules/b.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        a.name = "b"


      }),

紧接着,加载c模块。c.js中引入了a模块,此时a模块已在缓存中,再次加载直接返回缓存中的'./src/modules/a.js'对应值的exports,即{exports: {name: 'b'}}.exports。接着遇到console.log打印输出。

/***/ "./src/modules/c.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        console.log(a.name, 'a.name');


      })

总结

以webpack处理commonjs模块化为例,webpack将已加载过的模块对象存放在__webpack_module_cache__中。键名可以理解为模块名,键值可以简单理解为就是存储模块导出的对象。

未加载的模块存放在__webpack_modules__中。

每个模块代码其实是在一个函数里,webpack向该函数作用域内注入了module、module.exports、require这些对象,以供模块导入导出。导出的内容会挂载到对应的module.exports里。导入就是导入的module.exports对象。

由于本质是利用函数进行作用域隔离,对象进行变量共享。所以,修改导入模块的成员变量,一定会对使用该模块的地方产生影响。

最后贴出完整的webpack打包后代码

/******/ (() => { // webpackBootstrap
var __webpack_modules__ = ({

/***/ "./src/modules/a.js": ((module) => {

        const name = 'jyj'

        console.log('a加载了');

        module.exports = {
          name
        }

      }),

/***/ "./src/modules/b.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        a.name = "b"


      }),

/***/ "./src/modules/c.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

        const a = __webpack_require__(/*! ./a.js */ "./src/modules/a.js")

        console.log(a.name, 'a.name');


      })

});
/************************************************************************/
 	// The module cache
 	var __webpack_module_cache__ = {};
 	
 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
__webpack_require__(/*! ./modules/b.js */ "./src/modules/b.js")
__webpack_require__(/*! ./modules/c.js */ "./src/modules/c.js")
})();

/******/ })()
;
//# sourceMappingURL=index.bundle.js.map

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

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

相关文章

15_Scala面向对象编程_访问权限

文章目录 Scala访问权限1.同类中访问2.同包不同类访问3.不同包访问4.子类权限小结 Scala访问权限 知识点概念 private --同类访问private[包名] --包私有; 同类同包下访问protected --同类,或子类 //同包不能访问(default)(public)默认public --公…

Ubuntu MATE系统下WPS显示错位

系统:Ubuntu MATE 22.04和24.04,在显示器设置200%放大的情况下,显示错位。 显示器配置: WPS显示错位: 这个问题当前没有找到好的解决方式。 因为4K显示屏设置4K分辨率,图标,字体太小&#xff…

二、ArkTS语法学习

上一篇我们学习了Harmony​​​​​​开发入门,接下来我们简单学习一下ArkTS 一、ArkTS起源 在TS基础上扩展了申明式UI和状态管理等相应的能力 二、TS语法基础 1、类型 any 任意类型(动态类型) let a: any 5; a "str" num…

【笔记】常用USB转串口芯片CH340驱动自动静默安装方法

微信关注公众号 “DLGG创客DIY” 设为“星标”,重磅干货,第一时间送达。 前言 CH340是沁恒(南京沁恒微电子股份有限公司)一款非常有名USB转串口芯片,很多廉价的开发板上都使用这款USB转串口芯片,我觉得主要原因是因为它成本最低&a…

[论文阅读]Adversarial Autoencoders(aae)和代码

In this paper, we propose the “adversarial autoencoder” (AAE), which is a probabilistic autoencoder that uses the recently proposed generative adversarial networks (GAN) to perform variational inference by matching the aggregated posterior of the hidden …

基于K8S构建Jenkins持续集成平台

文章目录 安装和配置NFSNFS简介NFS安装 在Kubernetes安装Jenkins-Master创建NFS client provisioner安装Jenkins-Master Jenkins与Kubernetes整合实现Jenkins与Kubernetes整合构建Jenkins-Slave自定义镜像 JenkinsKubernetesDocker完成微服务持续集成拉取代码,构建镜…

LLM2Vec介绍和将Llama 3转换为嵌入模型代码示例

嵌入模型是大型语言模型检索增强生成(RAG)的关键组成部分。它们对知识库和用户编写的查询进行编码。 使用与LLM相同领域的训练或微调的嵌入模型可以显著改进RAG系统。然而,寻找或训练这样的嵌入模型往往是一项困难的任务,因为领域内的数据通常是稀缺的。…

Docker目录迁移

我们在生产环境中安装Docker时,默认的安装目录是 /var/lib/docker,而通常情况下,规划给系统盘的目录一般为50G,该目录是比较小的,一旦容器过多或容器日志过多,就可能出现Docker无法运行的情况,所…

QT+串口调试助手+扩展版

前言:此文章是这篇文章的拓展 QT串口调试助手基本版-CSDN博客,如果需要独立完成串口调试助手直接看基本版文章即可,如果需要完成串口调试助手的其他功能,参考拓展版。 一、更新QT串口调试助手UI界面 1、ui串口设置界面 2、ui串口…

树莓派4-使用systemctl设置开机自启oled播放服务ip地址与logo

一、目标: 开机自启oled显示服务ip与端口,并播放logo 二、过程: 1、出现luma库不存在问题,修改.service文件,增加用户与用户组。在本地测试过程中可以使用python script.py执行python脚本,所以将.servic…

机器学习周记(第三十七周:语义分割)2024.4.29~2024.5.5

目录 摘要 ABSTRACT 1 DeepLabV3 1.1 空间金字塔池化(ASPP) 1.2 解码器(Decoder) 1.3 Xception 2 相关代码 摘要 DeepLabV3 是由Google Brain团队开发的深度学习模型,专注于语义分割任务。它采用深度卷积神经网…

【c++算法篇】双指针(上)

🔥个人主页:Quitecoder 🔥专栏:算法笔记仓 朋友们大家好啊,本篇文章我们来到算法的双指针部分 目录 1.移动零2.复写零3.快乐数4.盛水最多的容器 1.移动零 题目链接:283.移动零 题目描述: 算法…

深究muduo网络库的Buffer类!!!

最近在学习了muduo库的Buffer类,因为这个编程思想,今后在各个需要缓冲区的项目编程中都可以用到,所以今天来总结一下! Buffer的数据结构 muduo的Buffer的定义如下,其内部是 一个 std::vector,且还存在两个…

9.3.k8s的控制器资源(deployment部署控制器)

目录 一、deployment部署控制器概念 二、deployment资源的清单编写 三、小结 功能 使用场景 原理 四、deployment实现升级和回滚 1.编辑deployment资源清单(v1版本) 2.创建service资源用于访问 ​编辑 3.修改deploy清单中pod镜像版本为V2 4…

Vmware虚拟机瘦身及Samba服务不可用问题解决

虚拟机磁盘空间膨胀是一个令人头疼的问题,特别是对许多搞开发的小伙伴。无论是做后台服务、嵌入式还是Android开发,都面临着这个难题。首先,操作系统本身就已占用不少空间,更新安装包,再下载一些开源软件,剩…

【管理篇】确定自己的管理风格

目录标题 常见的四类领导力风格不同领导力风格适应的场景领导力风格总结 常见的四类领导力风格 四类领导力风格,简单概况如下: 指令式管理:重事不重人,关注目标和结果,喜欢发号施令但不亲力亲为。支持式管理&#xf…

Windows编译SeetaFace6

1. 概述 SeetaFace6包含人脸识别的基本能力:人脸检测、关键点定位、人脸识别,同时增加了活体检测、质量评估、年龄性别估计,并且顺应实际应用需求,开放口罩检测以及口罩佩戴场景下的人脸识别模型。 发布时间 人脸识别算法版本 G…

易语言IDE界面美化支持库

易语言IDE界面美化支持库 下载下来可以看到,是一个压缩包。 那么,怎么安装到易语言中呢? 解压之后,得到这两个文件。 直接将clr和lib丢到易语言安装目录中,这样子就安装完成了。 打开易语言,在支持库配置…

swift微调多模态大语言模型

微调训练数据集指定方式的问题请教 Issue #813 modelscope/swift GitHubQwen1.5微调训练脚本中,我用到了--dataset new_data.jsonl 这个选项, 可以训练成功,但我看文档有提到--custom_train_dataset_path这个选项,这两个有什么…

C 认识指针

目录 一、取地址操作符(&) 二、解引用操作符(*) 三、指针变量 1、 指针变量的大小 2、 指针变量类型的意义 2.1 指针的解引用 2.2 指针 - 整数 2.3 调试解决疑惑 认识指针,指针比较害羞内敛,我们…