likes
comments
collection
share

Webpack+ESbuild实战记录,编译速度提升一倍

作者站长头像
站长
· 阅读数 15

摘要

由于团队内项目体积很大,webpack的打包速度非常之慢,因此最近在探索相关的优化方案。项目是使用create-react-app创建的,迁移到vite成本太高,因此采用了部分转换为esbuild-loader的方案,编译速度也有近一倍的提升。

分析工具

在优化之前,我们需要使用一些量化分析的工具,使用它们来帮助我们前后对比,确定优化指标

speed-measure-webpack-plugin 插件可以在打包后,于命令行输出各个插件和loader的耗时情况,我们可以看出哪些loaderplugin耗时比较久,然后对其进行优化

speed-measure-webpack-plugin官方文档的使用方式如下,但笔者使用这种写法时,出现严重拖慢打包速度的现象

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

module.exports = smp.wrap({ 
  // ...webpackConfig
})

改为如下写法后恢复正常

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')

module.exports = smp.wrap({ 
  plugins: [
    // ...plugins
    new SpeedMeasurePlugin()
  ]
})

反向优化的多进程打包

happypackthread-loader

  • happypack已经很多年没有维护了,核心原理是启用多进程,多个loader并行处理文件,happypack开发人员建议,如果使用webpack4及以上更推荐使用thread-loaderthread-loader做的事情和happypack一样

  • thread-loaderwebpack官方推荐的,原理是将loader放在单独的一个worker进程内处理,但实测下来babel-loader前放置thread-loader后的速度更慢了,原因如下

  1. thread-loader 实现是nodechild_process即子进程
  2. 系统开一个子进程的开销比新开一个线程worker_threads大的多
  3. 进程间通信也会消耗大量时间

使用 esbuild-loader

替换 babel-loader和ts-loader

esbuild的底层原理是用go生成的二进制文件处理jsts,速度比babel更快。如果没有使用babel插件,可以在webpack中直接用esbuild-loader替换babel

  module.exports = {
    module: {
      rules: [
-       {
-         test: /\.js$/,
-         use: 'babel-loader'
-       },
-       {
-         test: /\.tsx?$/,
-         use: 'ts-loader'
-       },
+       {
+         // Match js, jsx, ts & tsx files
+         test: /\.[jt]sx?$/,
+         loader: 'esbuild-loader',
+         options: {
+           // JavaScript version to compile to
+           target: 'es2015',
+           loader: 'jsx'
+         }
+       },

        ...
      ],
    },
  }

替换后,我们使用npm run serve实验一下,发现首次编译速度提升了一倍左右

Webpack+ESbuild实战记录,编译速度提升一倍 Webpack+ESbuild实战记录,编译速度提升一倍

二次编译的情况下,由于二者都有缓存,编译速度都有提升,不使用esbuild-loader时约42s,而使用esbuild-loader后达到了接近vite的秒开级

替换 TerserPlugin 和 CssMinimizerPlugin

esbuild也可以进行代码压缩

+ const { EsbuildPlugin } = require('esbuild-loader')

module.exports = {
    optimization: {
      minimizer: [
-       new TerserPlugin({
-         ...
-       }),
-       new CssMinimizerPlugin({
-          ...
-       }),
+       optimization: {
+         minimizer: [
+           new EsbuildPlugin({
+             target: 'es2015',
+             // 如果提取CSS并将其作为单独的文件发出
+             css: true
+           })
+         ]
+       },

        ...
      ],
    },
  }

替换后,运行npm run build打包,打包时间从67.5s减少到36s,依然是接近一倍的提升

Webpack+ESbuild实战记录,编译速度提升一倍 Webpack+ESbuild实战记录,编译速度提升一倍

但是观察打包产物,发现体积增大了约2%。通常情况下,为了节省构建时间让用户包增大是不能接受的,需要做出取舍。因此我们可以在生产环境构建压缩用terser节约空间,开发环境用esbuild节约时间。

参考文献