在 Compilation 阶段,模块会被加载 (loaded)、封存 (sealed)、优化 (optimized)、分块 (chunked)、哈希 (hashed) 和重新创建 (restored),Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以监听(watch)模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展,通过 Compilation 也能读取到 Compiler 对象。
Compilation 钩子
在 Compilation 中处理的对象分别是module
、chunk
、asset
,由 modules
组成chunks
,由chunks
生成assets
,处理顺序是:module → modules → chunks → assets
,先从单个 module
开始处理,查找依赖关系,最后完成单个 module
处理,完成全部modules
之后,开始 chunks
阶段处理,最后在根据优化配置,按需生成 assets
。整个 Compilation 的生命周期钩子虽然比较多,但是大规律上是围绕这个顺序进行的。
带 ★ 的是比较重要的钩子。
Compilation 和 Stats 对象
在 Webpack 的回调函数中会得到 stats
对象。这个对象实际来自于 Compilation.getStats()
,返回的是主要含有 modules
、chunks
和 assets
三个属性值的对象。
- modules:记录了所有解析后的模块
- chunks:记录了所有 chunk
- assets:记录了所有要生成的文件
Stats 对象本质上来自于 lib/Stats.js
的类实例,常用的方法 stats.hasWarnings()
、stats.hasErrors()
、stats.toJson()
和 stats.toString()
都可以在这个类里面找到对应的实现。
Stats 对象数据结构
Stats 对象的 JSON 数据结构,除了通过 Compilation.getStats()
获得,还可以在 webpack 回调中,通过 callback 参数获得:
webpack(config, (err, stats) => {
console.log(stats.toJson())
})
还可以通过 webpack-cli 的选项将本次打包的 stats 存入一个 json 文件:webpack --profile --json > compilation-stats.json
。
stats.toJson() 得到的数据结构格式如下:
{
'version': '5.0.0-alpha.6', // 用来编译的 webpack 的版本
'hash': '11593e3b3ac85436984a', // 编译使用的 hash
'time': 2469, // 编译耗时 (ms)
'filteredModules': 0, // 当 `exclude`传入`toJson` 函数时,统计被无视的模块的数量
'outputPath': '/', // path to webpack 输出目录的 path 路径
'assetsByChunkName': {
// 用作映射的 chunk 的名称
'main': [
'web.js?h=11593e3b3ac85436984a'
],
'named-chunk': [
'named-chunk.web.js'
],
'other-chunk': [
'other-chunk.js',
'other-chunk.css'
],
},
'assets': [
// asset 对象 (asset objects) 的数组
],
'chunks': [
// chunk 对象 (chunk objects) 的数组
],
'modules': [
// 模块对象 (module objects) 的数组
],
'errors': [
// 错误字符串 (error string) 的数组
],
'warnings': [
// 警告字符串 (warning string) 的数组
],
}
这里面除了编译基本信息、报错和 warnings 之外,就是modules
、chunks
和 assets
三个对应的数组,里面是单个的 module
、chunk
和 asset
。
Tips:stats.toJson
可以指定要不要输出对应的数据。例如不想输出 modules
和 chunks
,可以使用 stats.toJson(modules: false, chunks: false)
。
module
在每个 module 中,我们可以得到它的所有信息,这些信息可以分为四大类:
- 基本信息:包括最基本的内容、大小、id;
- 依赖关系:
module.reasons
对象描述了这个模块被加入依赖图表的理由,包含了引入的方式、引入的 module 信息及其对应代码在第几行第几列等,可以通过这个计算出 module 之间的依赖关系图表(graph); - chunks 和 assets 关系:
module.chunks
和module.assets
包含到 chunks 和 assets 中的对应 id 等; - 被 webpack 处理的后的信息:包含
module.failed
、module.errors
、module.warnings
等。
{
'assets': [
// asset 对象 (asset objects) 的数组
],
'built': true, // 表示这个模块会参与 Loaders , 解析,并被编译
'cacheable': true, // 表示这个模块是否会被缓存
'chunks': [
// 包含这个模块的 chunks 的 id
],
'errors': 0, // 处理这个模块发现的错误的数量
'failed': false, // 编译是否失败
'id': 0, // 这个模块的 ID (类似于 `module.id`)
'identifier': '(webpack)\\\\test\\\\browsertest\\\\lib\\\\index.web.js', // webpack 内部使用的唯一的标识
'name': './lib/index.web.js', // 实际文件的地址
'optional': false, // 每一个对这个模块的请求都会包裹在 `try... catch` 内 (与 ESM 无关)
'prefetched': false, // 表示这个模块是否会被 prefetched
'profile': {
// 有关 `--profile` flag 的这个模块特有的编译数据 (ms)
'building': 73, // 载入和解析
'dependencies': 242, // 编译依赖
'factory': 11, // 解决依赖
},
'reasons': [
// 见下文描述
],
'size': 3593, // 预估模块的大小 (byte)
'source': '// Should not break it...\\r\\nif(typeof...', // 字符串化的输入
'warnings': 0, // 处理模块时警告的数量
}
其中 module.reasons
数据结构如下:
{
'loc': '33:24-93', // 导致这个被加入依赖图标的代码行数
'module': './lib/index.web.js', // 所基于模块的相对地址 context
'moduleId': 0, // 模块的 ID
'moduleIdentifier': '(webpack)\\\\test\\\\browsertest\\\\lib\\\\index.web.js', // 模块的地址
'moduleName': './lib/index.web.js', // 可读性更好的模块名称 (用于 "更好的打印 (pretty-printing)")
'type': 'require.context', // 使用的请求的种类 (type of request)
'userRequest': '../../cases', // 用来 `import` 或者 `require` 的源字符串
}
chunk
在每个 chunk 中,信息也可以分为四大类:
- 基本信息:包括最基本的内容、大小、id;
- 来源:
chunk.origins
对象描述了这个模块被加入的理由,包含了引入的方式、引入的 module 信息及其对应代码在第几行第几列等,可以通过这个计算出 module 之间的依赖关系图表(graph); - 引用关系:
chunk.parents
和chunk.children
被引用和引用的 ids; - 包含和被包含:
chunk.files
和chunk.modules
包含到 assets 和自己包含 modules 中信息等。
{
'entry': true, // 表示这个 chunk 是否包含 webpack 的运行时
'files': [
// 一个包含这个 chunk 的文件名的数组
],
'filteredModules': 0, // 见上文的 结构
'id': 0, // 这个 chunk 的 id
'initial': true, // 表示这个 chunk 是开始就要加载还是 懒加载 (lazy-loading)
'modules': [
// 模块对象 (module objects) 的数组
'web.js?h=11593e3b3ac85436984a',
],
'names': [
// 包含在这个 chunk 内的 chunk 的名字的数组
],
'origins': [
// 下文详述
],
'parents': [], // 父 chunk 的 ids
// 生成 assets 的原因
'reason': 'split chunk (cache group: asyncVendors) (name: async)',
'hash': '170746935298270ad813',
// 自己引用谁
'children': [],
// 引用的顺序
'childrenByOrder': {},
'modules': [],
'rendered': true, // 表示这个 chunk 是否会参与进编译
'size': 188057, // chunk 的大小 (byte)
}
chunk.origins
对应的格式如下:
{
'loc': '', // 具体是哪行生成了这个 chunk
'module': '(webpack)\\\\test\\\\browsertest\\\\lib\\\\index.web.js', // 模块的位置
'moduleId': 0, // 模块的 ID
'moduleIdentifier': '(webpack)\\\\test\\\\browsertest\\\\lib\\\\index.web.js', // 模块的地址
'moduleName': './lib/index.web.js', // 模块的相对地址
'name': 'main', // chunk 的名称
'reasons': [
// 模块对象中`reason`的数组
],
}
asset
asset 相对简单一些,内容如下: