Webpack 原理与实战
Webpack 核心概念
Webpack 是一个现代 JavaScript 应用程序的静态模块打包器。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
核心概念
- Entry(入口):指示 Webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
- Output(输出):告诉 Webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。
- Loader:让 Webpack 能够去处理那些非 JavaScript 文件(Webpack 自身只理解 JavaScript)。
- Plugin:用于执行范围更广的任务,包括:打包优化、资源管理和注入环境变量。
- Mode(模式):通过选择
development、production或none之中的一个,来设置 mode 参数,你可以启用 Webpack 内置在相应环境下的优化。
Webpack 构建流程
- 初始化参数:从配置文件和 Shell 语句中读取并合并参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
Loader 原理
Loader 本质上是一个函数,接受源文件作为参数,返回转换的结果。
js
module.exports = function (source) {
// source 是源文件内容
// 对 source 进行处理
return modifiedSource
}多个 Loader 串行执行,顺序为从右到左(或从下到上)。
Plugin 原理
Plugin 是 Webpack 的支柱功能。Webpack 自身也是构建于你在 Webpack 配置中用到的相同的插件系统之上。
Plugin 可以访问 Webpack 提供的各种 API,通过监听生命周期事件来执行自定义逻辑。
Webpack 优化策略
构建性能优化
- 使用
include/exclude减少 Loader 搜索范围 - 使用
cache-loader或 Webpack 5 内置的持久化缓存 - 使用
thread-loader或parallel-webpack进行多进程打包 - 合理使用
DllPlugin和DllReferencePlugin
产物优化
- Tree Shaking:消除 dead code
- Code Splitting:代码分割,按需加载
- 压缩代码:TerserPlugin、CssMinimizerPlugin
- 提取公共代码:SplitChunksPlugin
Webpack 5 新特性
- 持久化缓存:默认开启,大幅提升二次构建速度
- Module Federation:模块联邦,实现跨应用共享模块
- Tree Shaking 增强:支持嵌套模块和 CommonJS 的部分 Tree Shaking
- Asset Modules:内置资源模块,替代 file-loader、url-loader、raw-loader
面试高频问题
1. Webpack 的热更新原理是什么?
Webpack HMR 的核心流程:
- Webpack Compile:编译阶段,生成 hash 值并创建更新清单
- HMR Runtime:浏览器端运行时,通过 WebSocket 与服务器建立连接
- 文件更新:文件变更后,Webpack 重新编译,推送更新通知
- 模块替换:浏览器端接收到更新,下载更新模块并替换旧模块
2. Loader 和 Plugin 的区别?
- Loader:文件加载器,用于对模块的源代码进行转换。它是一个转换器,将 A 文件进行编译形成 B 文件。
- Plugin:插件,用于扩展 Webpack 的功能。它是在 Webpack 生命周期中的特定时刻执行特定的任务。
3. 如何提高 Webpack 的构建速度?
- 升级 Webpack 版本(Webpack 5 持久化缓存)
- 多进程/多实例构建
- 缩小 Loader 作用范围
- 合理使用 resolve 配置
- 使用 DllPlugin 预编译资源
- 开启缓存
