跳到主要内容

Webpack 优化

体积分析

初级分析

可以通过官方提供的 stat.json 文件帮助我们分析打包结果,stat.json 文件可以通过下面语句快速生成:

webpack --profile --json > stats.json

接着我们通过官网提供的 stats.json 分析工具 进行分析,上传 stats.json 文件之后,就可以得到如下图所示分析结果:

其中包括 webpack 的版本、打包时间、打包过程的 hash 值、模块数量( modules )、chunk 数量、打包生层的静态文件 assets 以及打包的警告和错误数。 我们可以分析其提供的内容,进行大致问题的定位。 第三方工具 webpack-bundle-analyzer 是打包分析神器,它的界面个人觉得很好看,而且能很直观的给出每一个打包出来的文件的大小以及各自的依赖,能够更加方便的帮助我们对项目进行分析。

使用如下:

// config/webpack.common.js
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const commonConfig = {
plugins: [
new BundleAnalyzerPlugin({
analyzerPort: 8889, // 指定端口号
openAnalyzer: false,
}),
],
};

webpack-bundle-analyzer 其底层也是依赖 stat.json 文件的,通过对 stat.json 的分析,得出最后的分析页面 通过分析工具的分析,我们可以知道哪些文件耗时比较多,打包出来的体积比较大,从而对有问题的文件进行优化。

  • stat: 每个模块的原始体积
  • parsed: 每个模块经 webpack 打包处理之后的体积,比如 terser 等做了压缩,便会体现在上边
  • gzip: 经 gzip 压缩后的体积

速度分析

我们可以通过 speed-measure-webpack-plugin 这个插件帮助我们分析整个打包的总耗时,以及每一个 loader 和每一个 plugins 构建所耗费的时间,从而帮助我们快速定位到可以优化 Webpack 的配置。

如上图,耗时比较长的会以红色标出。 使用 引入此插件,创建一个 plugins 实例 smp 包裹 webpack 配置文件即可,我们修改一下 webpack 的公共配置文件 webpack.common.js:

// config/webpack.common.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
// ...
module.exports = (production) => {
if (production) {
const endProdConfig = merge(commonConfig, prodConfig);
return smp.wrap(endProdConfig);
} else {
const endDevConfig = merge(commonConfig, devConfig);
return smp.wrap(endDevConfig);
}
};

笔者文章演示的代码配置文件分为三个,分别为 开发环境配置文件,生产环境配置文件,以及前二者共用的公共配置文件,如下:

  • webpack.dev.js:开发环境使用的配置文件
  • webpack.prod.js:生产环境使用的配置文件
  • webpack.common.js:公共配置文件 执行打包之后,可以看到如下效果图:

注意:speed-measure-webpack-plugin 对于 webpack 的升级还不够完善,暂时还无法与你自己编写的挂载在 html-webpack-plugin 提供的 hooks 上的自定义 Plugin (add-asset-html-webpack-plugin 就是此类)共存,有人已经在 github 上提了 issue 了,但是貌似还是没有解决。

优化

体积优化

  • js 压缩
  • css 压缩
  • 去除无用 css
  • 图片压缩
  • 拆分代码
  • 使用预编译代码

速度优化

  • 分离开发、生产打包代码
  • 减少查找过程
    • 优化 resolve.extensions
    • 优化 resolve.modules 查找路径
    • 使用 resolve.alias 减少查找过程
  • 缩小构建目标
    • loader.inclue/loader.exclude
  • 利用多线程提升构建速度
    • HappyPack
      • 在 webpack4 中,可使用 happypack plugin (opens new window),但需要注意的是 happypack 已经久不维护了。
    • thread-loader 为官方推荐的开启多进程的 loader,可对 babel 解析 AST 时开启多线程处理,提升编译的性能。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "thread-loader",
options: {
workers: 8,
},
},
"babel-loader",
],
},
],
},
};
  • 预先编译资源模块(DllPlugin)
    • add-asset-html-webpack-plugin
  • 缓存 Cache 相关
    • babel-loader 开启缓存
    • terser-webpack-plugin 开启缓存
    • 使用 cache-loader 或者 hard-source-webpack-plugin
    • TerserPlugin cache 参数
    • webpack 5 内置缓存插件,可通过 cache 字段 (opens new window)配置开启。
      • 它将 Module、Chunk、ModuleChunk 等信息序列化到磁盘中,二次构建避免重复编译计算,编译速度得到很大提升。

如对一个 JS 文件配置了 eslint、typescript、babel 等 loader,他将有可能执行五次编译,被五次解析为 AST

  • acorn: 用以依赖分析,解析为 acorn 的 AST
  • eslint-parser: 用以 lint,解析为 espree 的 AST
  • typescript: 用以 ts,解析为 typescript 的 AST
  • babel: 用以转化为低版本,解析为 @babel/parser 的 AST
  • terser: 用以压缩混淆,解析为 acorn 的 AST

而当开启了持久化缓存功能,最耗时的 AST 解析将能够从磁盘的缓存中获取,再次编译时无需再次进行解析 AST。

得益于持久化缓存,二次编译甚至可得到与 Unbundle 的 vite 等相近的开发体验

在 webpack4 中,可使用 cache-loader (opens new window)仅仅对 loader 进行缓存。需要注意的是该 loader 目前已是 @deprecated 状态。

  • 合理使用 sourceMap
  • 更快的 loader
    • 在 webpack 中耗时最久的当属负责 AST 转换的 loader。
    • 当 loader 进行编译时的 AST 操作均为 CPU 密集型任务,使用 Javascript 性能低下,此时可采用高性能语言 rust 编写的 swc。
    • 比如 Javascript 转化由 babel 转化为更快的 swc (opens new window)

代码优化

  • 使用 ES6 Modules 语法,以保证 Tree-Shaking 起作用
  • 合理使用 Ployfill
  • 如果 icon 类图片太多的话,就使用雪碧图合成一张图片,减少网络请求,或者使用字体文件。
  • 此插件可以将一些公用包提取出来使用 cdn 引入,不打入 bundle 中,从而减少打包文件大小,加快打包速度。
  • 合理配置 chunk 的哈希值
  • browserslist 垫片体积控制

参考