前言
上一章我们讲了对webpack打包的代码进行整体分析。 本章我们将详细讲解webpack打包的代码是如何实现模块的导出和导入的,因为commonjs规范和esmodule规范存在一定的差异。 而esmodule的差别更大,所以我们先来分析一下CommonJS规范下的export和import
CommonJS 规范的导出和导入
示例代码
// 主模块入口
const data = require('./module.js')
const { param1, param2 } = require('./module.js')
console.log(data)
console.log(param1)
console.log(param2)
// module.js
module.exports = 'this is default export'
exports.param1 = 'export param1'
exports.param2 = 'export param2'
结果
进口
我们看一下打包后的文件导出规则与打包前的变化
可以听到。 打包后,webpack会进行以下处理:
也就是说,导入模块内容的功能是通过__webpack_require__方法来实现的,那么我们来看看__webpack_require__方法的逻辑是什么呢
// __webpack_require__
function __webpack_require__(moduleId) {
// installedModules 是一个模块缓存变量
// 先判断缓存内是否存在该模块
if (installedModules[moduleId]) {
// 存在,直接返回该模块的导出内容
return installedModules[moduleId].exports;
}
// 不存在,创建一个模块对象,定义一些初始值,并进行缓存
var module = installedModules[moduleId] = {
i: moduleId, // 模块id
l: false, // 加载标志
exports: {} // 导出内容
};
// modules是所有模块定义对象的集合,modules[moduleId]用于获取模块路径定义对象中对应模块id的键值,实际就是一个包含模块执行逻辑的匿名函数,最后执行该匿名函数并传递三个参数
// module, module.exports, __webpack_require__
// call方法只是用于改变this指向
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
// 返回模块的导出内容
return module.exports;
}
我们可以总结一下:
我们可以推测,__webpack_require__应该执行模块定义函数,在传入的参数module.exports上挂载一些变量,并在__webpack_require__方法最后返回module.exports,从而实现模块的导出
让我们看看是不是这样
出口
我们看一下打包后和打包前文件导入规则的变化
可以看到,打包前后的代码并没有实际的变化。 并且,正如我们所想,这个匿名函数内部将我们需要导入的内容挂载到了 module.exports 中webpack导出,那么当匿名函数被执行时(模块加载完成),需要导入的变量自然就被挂载到了模块中我们传入的对象,就实现了模块的导入。 至于为什么这里没有__webpack_require__参数webpack导出,是因为这个模块没有导出其他模块,所以不需要这个技巧。
总结
可以看到,虽然导入的核心方法是__webpack_require__方法,但是我们向该方法传递一个模块id,来查找该模块id对应的模块执行函数。通过向模块执行函数传递一个模块对象,让变量import 进去后挂载在 module.exports 上,并在 __webpack_require__ 方法最后返回 module.exports 即可实现模块的导入
以上内容仅供学习参考