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

起因

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 cachevar 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 neededexports: {}};// Execute the module function__webpack_modules__[moduleId](module, module.exports, __webpack_require__);// Return the exports of the modulereturn 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 cachevar __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.mzph.cn/bicheng/6914.shtml

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

相关文章

毕业设计参考-PyQt5-YOLOv8-鱼头鱼尾鱼长测量程序,OpenCV、Modbus通信、YOLO目标检测综合应用

“PyQt5-YOLOv8-鱼头鱼尾鱼长测量程序”是一个特定的软件程序,用于通过图像处理和目标检测技术来测量鱼类的长度。 视频效果: 【毕业设计】基于yolo算法与传统机器视觉的鱼头鱼尾识别_哔哩哔哩_bilibili 这个程序结合了多种技术: 1. OpenCV…

【Linux极简教程】常见实用命令不断更新中......

【Linux极简教程】常见实用命令不断更新中...... 常见问题1.Waiting for cache lock: Could not get lock /var/lib/dpkg/lock. It is held by process xxxx(dpkg) 常见问题 1.Waiting for cache lock: Could not get lock /var/lib/dpkg/lock. It is held by process xxxx(dp…

【Docker】★★★

docker 的网络模式 ●host模式:使用 --nethost 指定 容器与宿主机共享网络命名空间、ip和端口 ●container模式:使用 --netcontainer:NAME_or_ID 指定 新建的容器共享已有容器的网络命名空间、ip和端口 ●none模式:使用 --netnone 指定 不进行…

阿里实习生:面试阿里其实并没有那么难。

愉快的五一假期已经结束了, 又要投入到学习和工作当中了。 今天分享一位同学在阿里的Go后端实习面经详解, 希望对你有帮助。 Go里有哪些数据结构是并发安全的? 并发安全就是程序在并发的情况下执行的结果都是正确的; Go中数据类型分为两大类&#xff…

24年最新抖音、视频号0成本挂机,单号每天收益上百,可无限挂

详情介绍 这次给大家带来5月份最新的短视频挂机项目,简单易上手,而且不需要任何投入,经过测试收益非常可观,软件完全免费,特别适合没有时间但是想做副业的家人们

ai生图什么软件好?推荐7个不错的AI绘画软件

ai生图什么软件好?推荐7个不错的AI绘画软件!小编带你进入一个创意与科技交融的奇妙世界——AI绘画,让我们一同领略其中的魅力吧~ 一、爱制作AI 这款AI人工智能软件是一款多功能的工具平台,集合了AI问答、AI绘画和AI智能写作等功能…

在离线环境中将运行 Oracle DB 12c 的 CentOS 7.5 原地升级并迁移至 RHEL 7.9

《OpenShift / RHEL / DevSecOps 汇总目录》 说明 本文只是说明如何在 CentOS 7.5 上准备 Oracle DB 12c 验证环境,而将该环境升级并迁移至 RHEL 7.9 的操作过程请参见:《在离线环境中将 CentOS 7.5 原地升级并迁移至 RHEL 7.9》一文。 另外&#xff…

我希望未来10年,人工智能可以帮我解决这4件小事

生活在一线大城市的我,现在几乎整天被大数据、人工智能、机器学习、智慧生活的词汇环绕立体包围着,让我时刻感觉到,再过10年,我们五一假期真的可以摆脱现在擦肩接踵的旅游盛况了。但我其实要求倒是没这么高,我真心希望…

生产者与消费者 PV操作 与 阻塞队列

文章目录 普通方式 wait 与 notifyAll消费者生产者桌子测试类运行结果 阻塞队列Cook生产者Customer消费者测试类 普通方式 wait 与 notifyAll 消费者 package abc;public class Customer extends Thread{Overridepublic void run() {while (true) {synchronized (Desk.lock) {…

音频文件转文字怎么操作?3个方法手把手教你

音频文件转文字怎么操作?音频文件转文字,即将音频中的语音内容转换为可编辑和搜索的文本格式,是一项在多个场景中都非常实用的技术。无论是在会议记录、采访整理、学习笔记,还是视频字幕制作等场景中,音频转文字技术都…

吴恩达机器学习笔记:第 8 周-14降维(Dimensionality Reduction) 14.6-14.7

目录 第 8 周 14、 降维(Dimensionality Reduction)14.6 重建的压缩表示14.7 主成分分析法的应用建议 第 8 周 14、 降维(Dimensionality Reduction) 14.6 重建的压缩表示 在以前的视频中,我谈论 PCA 作为压缩算法。在那里你可能需要把 1000 维的数据压缩100 维特…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒解密恢复

网络在为企业提供便利的同时,也为企业的数据安全带来严重威胁。随着网络技术的不断发展,越来越多的企业利用网络开展各项工作业务,网络数据安全问题,一直成为企业关心的主要话题,但网络威胁随着网络技术的不断成熟&…

nginx--反向代理

反向代理 指的是代理外网用户的请求到内部的指定web服务器器,并将数据返回给用户的一种方式,这是用的比较多的一种方式 模块和功能 ngx_http_proxy_module: 将客户端的请求以http协议转发至指定服务器进行处理。ngx_stream_proxy_module&…

CDGA|企业的不同阶段如何做数据治理?

随着数字化时代的到来,数据已经成为企业运营和决策的核心要素。因此,数据治理成为企业在不同阶段必须面对的重要课题。 本文将探讨企业在初创期、成长期、成熟期和转型期四个阶段应如何进行数据治理。 初创期:建立数据治理基础 在初创期&…

美的集团太子惨败五一档,一顿火锅解决不了什么事

杨幂于谦的组合,很新鲜。 联丰策略拥有一支由知名互联网公司和国内证券金融机构的行业专家组成的一流运营团队。凭借他们在互联网产品开发和金融风险管理方面的丰富经验,我们的团队致力于为客户提供专业和个性化的证券交易服务。 当初,由丁晟导演的电影…

CUDA CPP Unity Compute Shader

为学 开始一个新的学习计划,涵盖: 主题学习内容CUDAProfessional CUDA C Programming/NVIDIA CUDA初级教程视频(周斌)CCPrimer / The Cherno CPPUnity Compute ShaderUdemy Learn to Write Unity Compute ShadersLinear AlgebraMIT 18.06 Prof.Gilbert…

Aigtek射频功率放大器的主要功能是什么

射频功率放大器是一种专门用于放大射频信号功率的电子器件,它在无线通信、雷达系统、卫星通信等射频应用中起着至关重要的作用。下面将详细介绍射频功率放大器的主要功能。 信号放大:射频功率放大器的主要功能之一就是将传入的射频信号放大到所需的功率水…

Flask与HTTP

一、请求响应循环 “请求-响应循环”:客户端发出请求,服务器处理请求并返回响应。 Flask Web程序的工作流程: 当用户访问一个URL,浏览器便生成对应的HTTP请求,经由互联网发送到对应的Web服务器。Web服务器接收请求&a…

【计算机网络】FTP站点配置搭建教程以及相关问题解决方案(超详细)

文章目录 1、安装Window Server 20082、搭建FTP环境(1)安装FTP服务器(2)配置FTP服务器(3)测试FTP连接 3、遇到的问题以及解决方案(1)Windows无法访问此文件夹(2&#xff…

上传文件到 linux

一、mac 法一:scp 先进入mac的 Node_exporter文件(要上传的文件)目录下 输入scp -P 端口号 文件名 rootIP:/存放路径 scp -P 22 node_exporter-1.8.0.linux-amd64.tar.gz root192.***.2:/root 法二、 rz mac 安装 lrzsz,然后…