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

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值