webpack源码分析—打包文件的整体分析
webpack可以说是目前最主流的项目打包工具,为了更加深入的理解其工作原理,后续的系列将会去刨析webbpack源码,以此来更加深入的学习webpack,并学习其中的思路。
打包文件分析
模块定义路径
webpack会将入口文件及其依赖的模块打包成一个文件。从图上面可以看到,webpack打包后的结果,实际是一个立即执行函数,函数接收一个modules的参数。这个参数是模块打包核心之一,我们展开来看一下
可以看到,传递的参数是一个对象类型的数据,而它的键值是一个类似路径的字符串,而键值则是一个匿名函数。这种结构是否有点熟悉,我们结合源代码来看一下
可以看到实际上该对象的键实际对应了我们模块的路径,而匿名函数的js代码则是对应的模块内的js执行代码。不难看出,webpack最终将模块路径作为键名,而对应的模块代码则使用一个匿名函数包装起来作为该路径键名的键值
总结
打包后的文件实际是一个立即执行函数,而传递的参数是入口模块及其依赖模块的路径和模块执行内容的一个集合,这里我叫他模块路径定义
// 模块路径定义
{
"模块的路径": (function() {
// 模块内的js代码
})
}
匿名函数的形参
模块路径定义的键值,是一个包含了模块执行代码的匿名函数,而这个匿名函数接收三个形参module
,exports
,__webpack_require__
module & exports
不难发现,在源代码中,我们实际是利用module.exports将模块内部的变量进行导出。
而在打包的代码,我们实际是将module传入模块内,然后将导出的值挂载到module的exports上,进而来实现变量的导出和暴露。
__webpack_require__参数
上面我们说了模块路径定义的键值实际是一个包含了模块内执行代码的匿名函数,但可以发现,它和源代码还是有一点差异的。
源代码导入模块的方式是通过require,而打包后的代码则是将导入模块的方法替换为__webpack_require__,这是webpack自己写的一个方法,主要是用于导入模块
总结
不管是commonjs还是esmodule在打包后导入导出的核心原理其实就是:
- 打包后模块化导出是靠将变量挂载到module.exports实现的
- 打包后模块化导入是靠__webpack_require__来进行导入
但webpack默认支持commonjs规范的导入导出规则,除了导入的方式进行修改以外,在代码的修改上是比较少的。
但对于esmodule,则是需要进行另外的操作,这个后面会讲到。
主逻辑代码
webpack打包后的文件是一个立即执行代码,并且接收一个参数,这个参数就是模块路径定义对象。接下来我们看看这个立即执行函数的主逻辑代码。
我们可以整体来看看,其实内部的逻辑并不是很复杂。无非就是一些变量和方法的定义,对于一些不是特别中要的方法定义我们先忽略
// 这是对加载过的模块进行缓存的对象
let installModules = {}
// 和__webpack_require__有关的定义问题,先忽略
// 执行入口
return __webpack_require__('./src/index.js')
在这个代码中,我们只需要看这两个执行即可。因为其他的一些定义现在看也没有意义。可以看到,在代码的最后,我们执行了一个__webpack_require__方法,并传入了一个路径字符串,这个路径就是我们的模块入口路径,下面再来看看__webpack_require__方法内部代码
__webpack_require__方法
__webpack_require__方法可以说是加载模块和导出模块变量的最核心方法,它可以加载某个模块,并返回模块内部导出的变量
// __webpack_require__方法接收一个moduleId参数
function __webpack_require__(moduleId) {
// 判断该模块是否缓存过
if (installedModules[moduleId]) {
// 存在直接返回对应的模块的导出
return installedModules[moduleId].exports;
}
// 创建一个module对象并进行缓存
var module = installedModules[moduleId] = {
i: moduleId, // 模块路径
l: false, // 加载标志
exports: {} // 导出对象
};
// 根据moduleId获取对应模块路径定义对象中对应的键值(匿名函数),并执行
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 设置加载标志
module.l = true;
// 返回导出的内容
return module.exports;
}
可以看出__webpack_require__方法主要就是进行模块的缓存和模块代码加载和模块内容导出的方法。在这段代码中,有两个比较重要的点
模块代码的加载和变量的导出
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
我们通过modules[moduleId]
找到对应路径下模块的执行代码。
并传递了module
, module.exports
,__webpack_require__
这三个参数。
- 通过传递
module.exports
来接收模块内部导出的变量 - 通过
__webpack_require__
来导入其他的模块的内容
return module.exports
__webpack_require__最终会返回我们在模块中导出的变量module.exports,这也是为什么我们可以使用 const name = __webpack_require__("./src/module.js")
的来接收模块导出变量的原因
图解
以上内容仅供学习参考