术语
- entry: 编译入口,webpack编译器点
- compiler: 编译管理器,webpack启动后会创建compiler对象,该对象一直存活直到结束退出
- compilation:单次编译过程的管理器,比如watch = true时,运行过程中只有一个compiler,但每次文件变更触发重新编译时,就会创建一个新的compilation
- dependence: 依赖对象,webpack基于该类型记录模块间依赖关系
- module:webpack内部所有资源都会以”module”对象形式存在,所有关于资源的操作、转译、合并都是以”module”为基本单位>进行的
- chunk:编译完成准备输出时,webpack会将module按特定的规则组织成一个一个的chunk,这些chunk某种程度上跟最终输出一一对应
- loader:资源内容转换器,实现从内容A转换B的转换器
- plugin:webpack构建过程中,会在特定的时机广播对应的事件,插件监听这些事件,在特定的时间点介入编译过程
webpack内部原理
主要部分:
- 项目中使用的每个文件都是一个模块,通过互相引用,这些模块会形成一个图数据结构。在打包过程中,模块会被合并成 chunk。 chunk 合并成 chunk 组,并形成一个通过模块互相连接的图。
chunk:
- 两种形式:
- initial(初始化) 是入口起点的 main chunk。此 chunk 包含为入口起点指定的所有模块及其依赖项。
- non-initial 是可以延迟加载的块。可能会出现在使用 动态导入(dynamic imports) 或者 SplitChunksPlugin 时。
- 每个 chunk 都有对应的 asset(资源)。
output :
- 输出文件的名称会受配置中的两个字段的影响:
- output.filename - 用于 initial chunk 文件
- output.chunkFilename - 用于 non-initial chunk 文件
- 在某些情况下,使用 initial 和 non-initial 的 chunk 时,可以使用 output.filename。
- 常用占位符:
- [id] - chunk id(例如 [id].js -> 485.js)
- [name] - chunk name(例如 [name].js -> app.js)。如果 chunk 没有名称,则会使用其 id 作为名称
- [contenthash] - 输出文件内容的 md4-hash(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js)
核心原理
核心对象
- 负责整体编译流程的Compiler对象、负责编译Module的Complication对象。
- 最核心的功能:将各种类型的资源,转译、组合、拼接、生成 JS 格式的
bundler 文件
。 - 这个过程核心完成了内容转换 + 资源合并两种功能, 三个阶段
初始化阶段
- 初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数
- 创建编译器对象:用上一步得到的参数创建 Compiler 对象
- 初始化编译环境:注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
- 开始编译:执行 compiler 对象的 run 方法
- 确定入口:根据配置中的 entry 找出所有的入口文件,调用compilition.addEntry 将入口文件转换为 dependence 对象
构建阶段
- 编译模块make:根据entry对应的dependence创建module对象,调用loader将模块转译为标准js内容,调用js解释器将内容转换为AST对象,从中找到该模块依赖的模块,在递归。处理全部入口文件
- 完成模块编译:通过上一步,得到每个模块被翻译后的内容以及它们之间的依赖关系图
生成阶段
- 输出资源seal:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 写入文件系统emitAssets:根据配置确定输出的路径和文件名,把文件内容写入到文件系统
- 参考
PLugin解析
什么是插件?
- 从形态上看,插件通常是一个带有aplly函数的类 ,apply运行时会得到参数compiler,以此为起点调用hook
什么时候触发什么钩子?
- compiler.hooks.compilation
- 触发时机:启动编译创建出 compilation 对象后触发
- 传递参数:当前编译的 compilation 对象
- 示例:很多插件基于此事件获取 compilation 实例
- compiler.hooks.make
- 触发时机:正式开始编译时触发
- 传递参数:同样是当前编译的 compilation 对象
- 示例:webpack 内置的 EntryPlugin 基于此钩子实现 entry 模块的初始化
- compilation.hooks.optimizeChunks
- 触发时机:seal 函数中,chunk 集合构建完毕后触发
- 传递参数:chunks 集合与 chunkGroups 集合
- 示例:SplitChunksPlugin 插件基于此钩子实现 chunk 拆分优化
- compiler.hooks.done
- 触发时机:编译完成后触发
- 传递参数:stats 对象,包含编译过程中的各类统计信息
- 示例:webpack-bundle-analyzer 插件基于此钩子实现打包分析
- compilation.seal函数内部有以下两个钩子
- optimizeModules: 优化已经编译出的modules
- afterOptimizeModules:用于通知优化行为的结束
- apply从设计上只有输入,webpack不关心输出,所以在插件中只能调用类型实体的方法或更改实体的配置信息,来变更编译行为
如何让影响编译状态?
- hooks回调内部通过修改状态、调用上下文api等方式对webpack产生side effect
- webpack会将上下文信息以参数或this(compiler对象)形式传递给钩子回调,在回调中可以调用上下文对象的方法或者直接修改上下文对象属性的方式,对原定流程产生side effect