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 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于若依框架搭建网站的开发日志(一):若依框架搭建、启动、部署

RuoYi(基于SpringBoot开发的轻量级Java快速开发框架) 链接:开源地址 若依是一款开源的基于VueSpringCloud的微服务后台管理系统(也有SpringBoot版本),集成了用户管理、权限管理、定时任务、前端表单生成等…

You don’t have permission.

The document “XXX” could not be saved. You don’t have permission. 1.查看修改了iOS系统库导致的, 根据提示, 进入到"XXX"文件中, 然后commandz回退/取消 2. Xcode 调试遇到的报错(持续更新)

治疗耳鸣患者案例分享第二期

“患者耳鸣20年了,目前耳朵没有堵或者胀的感觉,但是偶尔有点痒,平时会有头晕头胀这种情况,然后头晕是稍微晕炫一下。然后头疼是经常有的,头胀不经常。” 患者耳鸣持续20年,虽然耳朵没有堵或胀的感觉&#x…

书生浦语训练营第三次课笔记:XTuner 微调 LLM:1.8B、多模态、Agent

Finetune 简介 两种Finetune范式:增量预训练微调、指令跟随微调 微调数据集 上述是我们所期待模型回答的内容,在训练时损失的计算也是基于这个。 训练数据集看起来是这样,但是真正喂给模型的,是经过对话模板组装后的 下图中&…

防火墙的基本概念

我们在 TCP/IP协议四层模型与OSI七层模型 的最后说过,在四层模型中每一层都会有对应的风险,而防火墙就是来阻断这些风险的工具。 防火墙的基本功能 防火墙的分类 目前没有权威而明确的分类 按照实现方式: 硬件防火墙软件防火墙 按照部署…

HNU-人工智能-实验1-A*算法

人工智能-实验1 计科210x 甘晴void 一、实验目的 掌握有信息搜索策略的算法思想; 能够编程实现搜索算法; 应用A*搜索算法求解罗马尼亚问题。 二、实验平台 课程实训平台https://www.educoder.net/shixuns/vgmzcukh/challenges 三、实验内容 3.…

高扬程水泵助力森林消防,守护绿色生命线/恒峰智慧科技

随着人类社会的不断发展,森林资源的保护和管理变得越来越重要。然而,森林火灾却时常威胁着这一宝贵资源。为了有效应对森林火灾,提高灭火效率,高扬程水泵在森林消防中发挥了重要作用。本文将重点介绍高扬程水泵在森林消防中的应用…

AI终端设备的自动化分级

摘要: AI智体被定义为感知环境、做出决策和采取行动的人工实体。 受SAE(汽车工程师学会)自动驾驶6个级别的启发,AI智体也根据效用和强度进行分类,分为以下几个级别: L0——无AI,有工具&#xf…

机器学习中线性回归算法的推导过程

线性回归是机器学习中监督学习中最基础也是最常用的一种算法。 背景:当我们拿到一堆数据。这堆数据里有参数,有标签。我们将这些数据在坐标系中标出。我们会考虑这些数据是否具有线性关系。简单来说 我们是否可以使用一条线或者一个平面去拟合这些数据的…

力扣每日一题111:二叉树的最小深度

题目 简单 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明:叶子节点是指没有子节点的节点。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:2示例 2&#x…

SpringCloud生态体系介绍

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。 必要说…

【面试经典 150 | 图】除法求值

文章目录 写在前面Tag题目来源解题思路方法一:广度优先搜索 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容…

2024年第二十六届“华东杯”(B题)大学生数学建模挑战赛|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时,你是否曾经感到茫然无措?作为2022年美国大学生数学建模比赛的O奖得主,我为大家提供了一套优秀的解题思路,让你轻松应对各种难题。 让我们来看看华东杯 (B题)! 第一个问题…

CI/CD笔记.Gitlab系列.新用户管理

CI/CD笔记.Gitlab系列 新用户管理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_285502…

使用CNN或resnet,分别在flower5,flower17,flower102数据集上实现花朵识别分类-附源码-免费

前言 使用cnn和resnet实现了对flower5,flower17,flower102数据集上实现花朵识别分类。也就是6份代码,全部在Gitee仓库里,记得点个start支持谢谢。 本文给出flower17在cnn网络实现,flower102在resnet网络实现的代码。…

正则表达式-前瞻和后顾

正则表达式中的前瞻和后顾。 前瞻(Lookahead) 前瞻是一种断言,它会检查在当前位置之后是否存在某种模式,但不会实际匹配该模式。前瞻有两种形式: 正向前瞻 (?pattern) 检查当前位置之后是否存在指定的模式如果存在,则匹配成功,但不会消耗该模式例如 \w(?\d) 将匹配后面跟数…

Mysql 8.0.33 迁移至 Postgresql 16.2

小伙伴们,你们好,我是老寇,我又回来,几个月不见,甚是想念啊!!!! 这不,云平台需要改造,将Mysql替换成Postgresql,话说回来&#xff0c…

步态识别论文(6)GaitDAN: Cross-view Gait Recognition via Adversarial Domain Adaptation

摘要: 视角变化导致步态外观存在显着差异。因此,识别跨视图场景中的步态是非常具有挑战性的。最近的方法要么在进行识别之前将步态从原始视图转换为目标视图,要么通过蛮力学习或解耦学习提取与相机视图无关的步态特征。然而,这些方法有许多约…

【管理篇】如何处理团队里的老资格员工和高能力员工?

目录标题 两类员工对比🤺老资格员工高能力员工 作为领导你应该怎么做? 在管理团队时,处理老资格员工和高能力员工是一项至关重要的任务。这两类员工在团队中扮演着不同的角色和有着不同的需求,因此需要针对性的管理和激励。下面将…

程序设计——前后端分离实现简单表白墙

文章目录 一、前端页面样式代码二、前后端衔接1. 后端创建 maven 项目2. 针对前后端交互的解释以及后端代码的实现针对 post 请求解释前后端衔接针对 Get 请求解释前后端衔接 3.后端与数据库的联系以及对数据的存取单独封装数据库连接代码解释后端存储 save 数据的代码解释后端…