webpack
基础
参考
核心概念
- entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入
- module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块
- chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割
- loader:文件转换器
- plugin:插件,执行特定任务
- output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果
Loaders
从右往左,从下往上
执行
- Loaders - 官网
- 常用 Loaders - 深入浅出 Webpack
- Loader Interface - 官网
- 编写 Loader - 深入浅出 Webpack
pitch
:拦截 loader 的执行,预处理
js
// 自己写个,style-loader
// 作用:将css内容,通过style标签插入到页面中
// source为要处理的css源文件
function loader(source) {
let style = `
let style = document.createElement('style');
style.setAttribute("type", "text/css");
style.innerHTML = ${source};
document.head.appendChild(style)`
return style
}
module.exports = loader
Plugins
- Plugins - 官网
- 常用 Plugins - 深入浅出 Webpack
- Plugin API - 官网
- 编写 Plugin - 深入浅出 Webpack
区别
- loader 运行在打包文件之前
对于 loader,实质是一个转换器,将 A 文件进行编译形成 B 文件,操作的是文件,比如将 A.scss 或 A.less 转变为 B.css,单纯的文件转换过程
- plugin 在整个编译周期都起作用
在 Webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果
source map
- Webpack Devtool - 官网
- JavaScript Source Map 详解 - 阮一峰
tree-shaking
V5
参考
Asset Modules
Long Term Caching
cache
URIs
TreeShaking 和 SideEffects
模块联邦
使 JavaScript 应用,得以从另一个 JavaScript 应用中动态的加载代码,实现共享依赖,用于前端的微服务化
比如项目 A 和项目 B,公用项目 C 组件,以往这种情况,可以将 C 组件发布到 npm 上,然后 A 和 B 再具体引入。当 C 组件发生变化后,需要重新发布到 npm 上,A 和 B 也需要重新下载安装
使用模块联邦后,可以在远程模块的 Webpack 配置中,将 C 组件模块暴露出去,项目 A 和项目 B 就可以远程进行依赖引用。当 C 组件发生变化后,A 和 B 无需重新引用
PackageExports
核心流程
参考
- 二十张图片彻底讲明白 Webpack 设计理念,以看懂为目的
- 工作原理概括 - 深入浅出 Webpack
- 万字总结 一文吃透 Webpack 核心原理 - 5
- 一文掌握 Webpack 编译流程 - 4
- 【webpack 进阶系列】Webpack 源码断点调试:核心流程 - 4
- 【webpack 进阶系列】手撸一个 mini-webpack(一) : 分析收集依赖
- 【webpack 进阶系列】手撸一个 mini-webpack(二) : 打包依赖代码
流程
TIP
webpack-cli
是要安装的,webpack
命令执行时会先查找其是否安装
Webpack 的构建流程可以分为以下三大阶段:
- 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。
- 编译:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理。
- 输出:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。
以下来自万字总结文章
初始化
- 初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数
- 创建编译器对象:用上一步得到的参数创建 Compiler 对象
- 初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
- 开始编译:执行 compiler 对象的 run 方法
- 确定入口:根据配置中的 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象

构建
- 编译模块(make):根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:上一步递归处理所有能触达到的模块后,得到了每个模块被翻译后的内容以及它们之间的 依赖关系图

生成
- 输出资源(seal):根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 写入文件系统(emitAssets):在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

HMR
参考
- HMR(HotModuleReplacement) - webpack
- webpack-dev-server - GitHub
- webpack-dev-middleware - GitHub
- WebSocket
- memfs - GitHub
不同
webpack-dev-server
自动安装配置webpack-dev-middleware
,使用 WebSocketwebpack-dev-middleware
搭配webpack-hot-middleware
,手动配置,使用 SSE
流程


webpack-dev-server
提供 http server 服务和 socket server 服务,在webpack-dev-middleware
中间件中启用 webpack 的 watch 模式。- 当文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码保存在文件系统中。
webpack-dev-server/client/index.js
在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的状态信息告知浏览器端。- 浏览器端通过
webpack/hot/dev-server.js
接收到最新的编译信息,从而触发module.hot.check
方法来开始热更新检查。 - 执行
webpack/lib/hmr/HotModuleReplacement.runtime.js
中的hotCheck
方法,通过hmrDownloadManifest
以 fetch 方式请求hot-update.json
文件,然后执行 loadScript 以 jsonp 的方式加载hot-update.js
文件。 - 执行
hot-update.js
文件中的self[‘webpackHotUpdate’]
方法,在webpack/lib/web/JsonpChunkLoadingRuntimeModule.js
文件的self[‘webpackHotUpdate’]
方法定义中去收集到需要更新的模块依赖。 - 调用
internalApply
方法,用新模块替换掉旧模块,然后执行module.hot._acceptedDependencies
中的回调函数,从而实现模块替换的效果。
构建性能
参考
- build-performance - 官网
- 学习 Webpack5 之路(优化篇)
- 优化 - 深入浅出 Webpack
分类
- speed-measure-webpack-plugin - 构建速度
- webpack-bundle-analyzer - 构建体积
查找并诊断性能瓶颈:
- 构建速度分析:影响构建性能和开发效率
- 构建体积分析:影响页面访问性能
构建性能优化常用方法:
- 通过多进程加快构建速度
- 通过分包减少构建目标容量
- 减少构建目标加快构建速度
构建速度优化
优化 loader 配置
优化正则匹配,使用 includ、exclude、test 指定需要处理的文件,忽略不需要处理的文件
对于 loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。
js
module: {
rules: [{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src'),
}]
},
减少 loader、plugin
- Asset Modules - 官网
每个的 loader、plugin 都有其启动时间。尽量少地使用工具,将非必须的 loader、plugin 删除。
优化 resolve 配置
- Resolve - 官网
多进程/多线程
- thread-loader - 官网
- terser parallel - 官网
开启缓存
- cache - 官网
DLL
使用:
- 分包:定义 webpack.dll.config.js,使用 DllPlugin 配置分包,定义 scripts 命令,执行命令,完成分包
- 排除分包:在 vue.config.js 中使用 DllReferencePlugin 引用 manifest 文件排除分包
- 拷贝 dll:将 dll 拷贝到项目目录下
- 引用 dll:使用 add-asset-html-webpack-plugin 引用分包文件
构建体积优化
代码压缩
对 js 文件进行压缩,从而减小 js 文件的体积,还可以压缩 html 、css 代码。
代码分离
Tree Shaking
- Tree Shaking - 官网
- purgecss-webpack-plugin - Github