概念
webpack编译过程中一个比较重要的概念compiler、compilation
- Compiler类: webpack 的主要引擎,
compiler
对象是一个全局单例,在compiler
对象记录了完整的webpack环境信息,在webpack从启动到结束,compiler
只会生成一次。 - Compilation类:代表每一次构建的上下文对象。每次热更新和重新构建,
compiler
都会重新生成一个新的compilation
对象,负责此次更新的构建过程。一个compilation
对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。
涉及术语
- module:模块,分割的代码单元,webpack中的模块可以是任何内容的文件,不仅限于JS
- chunk:webpack内部构建模块的块,一个chunk中包含多个模块,这些模块是从入口模块通过依赖分析得来的
- bundle:chunk构建好模块后会生成chunk的资源清单,清单中的每一项就是一个bundle,可以认为bundle就是最终生成的文件
- hash:最终的资源清单所有内容联合生成的hash值
- chunkhash:chunk生成的资源清单内容联合生成的hash值
- chunkname:chunk的名称,如果没有配置则使用main
运行流程
Webpack的运行流程是一个串行的过程,从启动到结束依次执行以下流程:
- 初始化,启动构建,读取与合并配置参数,实例化 Compiler,加载 Plugin。
- 编译,从入口文件出发,对每个模块文件调用对应的Loader进行转换,生成AST并记录依赖模块,根据依赖模块递归加载。
- 输出,对编译后的模块组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。
初始化阶段
- 读取
webpack
的配置参数,将CLI参数、配置文件、默认配置进行融合,形成一个最终的配置对象。 - 启动
webpack
,创建Compiler
对象; - 注册所有的自定义插件
- 调用
environment
钩子 、afterEnvironment
钩子 - 注册所有
webpack
内置的插件
编译阶段
- 创建新的
Compilation
对象 - 从入口文件(
entry
)开始解析,读取cache中是否已经有了相同hash的资源,如果有,则直接返回内容,否则才会继续执行模块生成的逻辑,并存入cache中。 - 对不同文件类型的依赖模块文件使用对应的
Loader
进行编译,最终转为Javascript
文件; - 通过
acorn
库生成AST语法树
,找到其导入的依赖模块,并记录在module.dependencies
数组。 - 保存转换后的代码并记录在
compilation.assets
和compilation.chunks
中
输出阶段
- 触发
shouldEmit
钩子,询问插件哪些文件需要输出,哪些不需要 - 触发
emit
钩子,输出asset
到 output 目录之前执行。插件在这里可以修改输出内容。 - 触发
after-emit
钩子,代表文件输出完成。 - 触发
done
钩子,成功完成一次完整的编译和输出流程。
整个过程中webpack
会通过发布订阅模式,向外抛出一些hooks
,webpack
的插件即可通过监听这些关键的事件节点,执行插件任务进而达到干预输出结果的目的。
webpack编译结果分析
最终Webpack
打包出来的bundle
文件是一个IIFE
的执行函数。
// webpack 5 打包的bundle文件内容
(() => { // webpackBootstrap
var __webpack_modules__ = ({
'file-A-path': ((modules) => { // ... })
'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // ... })
})
// 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;
}
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./src/index.js");
})
在上面的打包demo
中,整个立即执行函数里边只有三个变量和一个函数方法,__webpack_modules__
存放了编译后的各个文件模块的JS内容,__webpack_module_cache__
用来做模块缓存,__webpack_require__
是Webpack
内部实现的一套依赖引入函数。最后一句则是代码运行的起点,从入口文件开始,启动整个项目。