Skip to content

性能优化

构建优化

你是如何优化webpack构建时间的?具体怎么量化的?

  • 为什么不用vite: Vite 可以直接跳过打包的步骤,但是因为我们这个项目基于 webpack,体量很大,里面用到了很多 webpack 生态的插件和 loader,冒然迁移到 Vite 可能会带来一些未知的风险,例如在 Vite 生态中找不到对标的插件,那么我们就需要在 Vite 中通过自定义插件来实现那个插件的功能,这个工作量是不可预估的
  • 在 webpack 中有一个插件 speed-measure-webpack-plugin,我就是通过这个插件去查看的构建时间,它会出一份报告,包含总体构建时间、各阶段的耗时、插件耗时、loader 耗时,这样我就能非常清楚究竟是哪些地方耗时。
  • 通过插件的分析结果来看,我发现 Babel 在编译 JS 时特别耗时,还有就是一些 loader,比如处理 CSS 的 css-loader,在解析和处理过程中也挺耗时的。因此我考虑的主要优化方案有:
    • 用 swc 替换 babel 进行编译工作
    • thread-loader 解决 loader 解析耗时问题
  • 另外还配合了一些额外的优化手段。(钩子🪝)最终落地的方案效果非常好,再次用 speed-measure-webpack-plugin 插件进行构建时间分析,基本上构建时间在 10 多秒左右。

你说你用到了 swc,那么就先讲一讲这个吧,什么是 swc,它的优势有什么?你为什么用它来替换 babel ?

  • swc 是一个用 rust 写的 JS/TS 编译器,因为基于 rust,所以编译速度非常快,而且 swc 能够兼容大多数 babel 插件和配置,因此迁移起来没有太高的成本。(做一个简单的介绍)
  • 做开始做优化之前,我们项目的构建时间差不多要花费 8~10分钟左右,替换为 swc 编译后,构建时间减少到了约 3 分钟左右。(优化前后的区别)
  • 所以整个优化方案中 swc 是最重要,占大头的。而 loader 解析耗时的问题,我是通过 thread-loader 来解决的

thread-loader它是如何解决 loader 解析耗时问题的?

  • thread-loader 可以通过多线程并行处理 loader 操作,这样就减少了主线程的负载。
  • 当我们用了 thread-loader 以后,处理图片、CSS 相关 loader 的耗时问题也就解决了,构建时间进一步缩减到了 2 分钟。

还有什么其他的优化手段

  • 升级 webpack5 新提供的持久化缓存技术,能够把模块的编译结果、解析结果以及插件的执行结果缓存到内存或者文件里面,这样后续进行构建的时候就可以重用这些缓存数据,减少不必要的重复计算和编译。当然,第一次启动项目的时候,时间上面没有太大变化,因为缓存还没有生成。
  • 生产环境下取出hash
  • 升级一些老旧的插件,因为一些插件新版本相较于旧版本,会修复一些 bug,在性能、算法上面可能也会有一些提升
    • terser-webpack-plugin 这个插件是做压缩的。因为打包的时候有一个重要的任务就是要压缩,而压缩所花费的时间也挺长的,我就琢磨着能不能减少一下压缩所花费的时间。看到了这个插件的 CHANGELOG 文档,发现从 5.2.0 版本开始引入了 swc 压缩器,我就估摸着性能上面能有大幅的提升,于是我就对这个插件进行了版本升级,不出所料,压缩的时间再一次得到了优化

辅助分析插件

错误提示优化: friendly-errors-webpack-plugin:更好的错误提示 node-notifier:调用的系统的右下角提示弹窗

speed-measure-webpack-plugin:打包性能分析, 查看构建时间,包含总体构建时间、各阶段的耗时、插件耗时、loader 耗时。 speed-measure-webpack5 -plugin

webpack-bundle-analyzer-plugin:打包结果分析

genertate stats file配置项生成打包报告

webpack --progress

webpack-optimizer-plugin

缩小范围

配置, 控制引入文件时匹配的范围

json
resolve: {
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], 
}

忽略模板

noParse: 配置,让一些模块被忽略,不解析,

Webpack.IgnorePlugin:忽略模块,不解析

缓存与线程

js
use: [
  {
      // 开启多线程,线程开启结束也要消耗资源,除非项目真的很大,否则没不要
    loader: "thread-loader",
    options: {
      workers: 1,  // 开启几个worker来打包
    },
  },
]

压缩

json
optimization: {
  minimize: true,
  minimizer: [
    new TerserPlugin({
      extractComments: false,
      ...
    }),
  ],
}

image-minimizer-webpack-plugin

mini-css-extract-plugin

purge-css-webpack-plugin

tree-shaking

默认的

代码分割,提取公共代码

webpackPrefetch: true 让懒加载的组件自动添加上script prefetch

基础库 共用代码

json
optimization: {
  splitChunks: {
    chunks: 'all'// 默认async只分割异步模块
  }
}

苏ICP备2025160170号-1 | 前端进化之路 | Released under the MIT License.