如何让老项目的打包构建时间减少70%?

avatar
Web前端 @CVTE_希沃

希沃ENOW大前端

公司官网:CVTE(广州视源股份)

团队:CVTE旗下未来教育希沃软件平台中心enow团队

本文作者:

小羽名片.png

Hello,大家好,我是小羽同学,一个平凡而又不甘于平凡的前端开发工程师。

今天的话,主要是想和大家聊聊小羽是怎么把一个react老项目构建时间减少70%+的。

小羽最近接手了一个3年前的老项目

看了下版本,react 16.0 + ts + webpack4,感觉还行

但是听说中间有接近一年的时间没人维护,原本熟悉项目的人都溜了。

image-20210621194704744-4276028.png

还没来的及详细研究代码,需求就到了。

只能硬着头皮上了,还好有我的导师兵哥从旁协助。

前期的开发也算是有惊无险的渡过了吧。

最近几天刚开发完一个版本,然后发现我们项目的构建时间实在太久了

XDM,如果我说打包一个react项目居然要150s,你们敢相信吗?

小羽当时也是懵了

147s,这可是2分半的时间。。。

image-20210621194742994-4276065.png

image-20210619103016762.png

image-20210619101825200.png

看了一下cpu的利用率,平均下来大概就是20%左右。这个利用率也太低了吧?完全没有把cpu的性能发挥出来。

happypack

然后小羽找了下webpack打包优化相关的包。然后发现了网上大多数都是说happypack这个插件可以进行多线程的优化。

那就整起呗。

image-20210621194852686-4276134.png

npm install happypack --save-dev

配置大概如下

const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({size: require('os').cpus().length})

module.exports = {
    ...
    module: {
        rules: [
            test: /\.js$/,
            use: ['happypack/loader?id=js'],
        ],
        plugins: [
          ...,
          new HappyPack({
              id: 'js',
              loaders: ['babel-loader'],
  						threadPool: happyThreadPool,
          })
      ]
    }
}

运行了一下

嗯,还不错,单js的转译就减少了20s

thread-loader

然而,小羽在css/less打包优化的时候遇到了问题。

原因是css/less中用到了mini-css-extract-plugin这个样式抽离插件,删除这个插件后打包又是正常的。

查阅npm中的文档,发现作者由于对改项目的兴趣消退,在两年前已经断更了。。。

然后作者给我们推荐了thread-loader,那就转到thread-loader中去看看吧。

image-20210621164930138.png

thread-loader中提到,thread-loader中是无法使用自定义加载器 API(即通过插件)。

而loader的加载顺序是从右往左从下往上加载的。

像下面这个loader的加载顺序则是less-loader => css-loader => style-loader

{
  	test: /\.less$/,
    use: [
      'style-loader',
      'css-loader', 
      'less-loader'
    ],
},

那么问题解决,只要我们在引用mini-css-extract-plugin前加载thread-loader就不会报错了。

完成所有的配置后,咱们再来尝试一下。

添加了thread-loader的优化后打包构建的时间大概来到了120s左右,和未优化前相比,减少了25-30s

image-20210621162853154.png

然后cpu的利用率也终于起来了,在loader编译阶段,利用率可以提升到40%多。

image-20210621162647423.png

terser-webpack-plugin

可是120s的时间还是很长,这时候怎么处理呢?

其实在webpack的打包构建中耗时较多的两个步骤应该是loader的转译以及代码的混淆压缩。(前提是得有这两个)

转译已经搞定了,那咱们处理混淆压缩吧。

看了下项目,发现用的是uglifyjs-webpack-plugin这个插件进行混淆压缩。

那就看下官网吧

嗯,你确定没有在逗我吗?又是两年前断更了,这。。。

image-20210621164838233.png

我不想优化了!!!

都是些啥玩意啊!!!

image-20210621195043046.png

哈哈哈,开个玩笑

优化那是必须得继续的。那就只能够继续查阅资料啦。

一个偶然的机会,发现了咱们接下来的主角——terser-webpack-plugin

记住,如果是webpack4的版本那么也要安装terser-webpack-plugin v4的版本。v5的版本是给webpack5来使用的。

image-20210621170220107.png

小羽这边接受的项目是webpack4,那就用v4的,然后看了下插件的版本。v4最新的是4.2.3

npm install terser-webpack-plugin@4.2.3 --save-dev

const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
	...
	optimization:{
    minimizer: [
      new TerserPlugin({
        parallel: require('os').cpus().length - 1,
        terserOptions:{
          compress:{
            inline:false
          },
          mangle: {
            safari10: true
          }
        }
      })
    ]
  }
}

parallel参数是用来设置你启动的线程数的。

require('os').cpus().length - 1,代表获取你电脑所有线程-1的数量。

然后这里得提一下,并不是启动的线程数量越多就越好的(包括前面的thread-loader)。

因为我们每启动一个线程都是需要消耗一定的时间的,这个时间约为600ms

举个🌰,假如现在你用的是一台8核16线程的电脑。咱们把所有的线程都启动起来,那么启动的时间约为9.6s

但是当咱们的项目比较小,原本打包的时间可能就5、6s,压缩的时间可能连600ms都不到,这样的话咱们的打包优化就变成了负优化了。

所以并不是每一个项目都需要开启多线程的。是否要开启?开启多少个线程?这些都是需要看情况的。

terser-webpack-pluginterserOptions参数基本与uglifyjs-webpack-plugin的uglifyOptions是一致的,直接平滑升级,美滋滋~

那咱们来测试一下吧~

构建时间约为41s,nice~

image-20210621174356550.png

然后在看一下咱们混淆压缩时候的cpu负载也是彪了上来,cpu负载最高可到92%

image-20210621173705668.png

最后咱们计算一下

未优化前147s,优化后41s,一共减少了106s,减少了快两分钟的时间。

那咱们在计算一下提升的性能,(147-41)/147≈0.72。提升了72%

image-20210621195143493.png

小结

小羽在本文中和小伙伴们聊了下在工作中是如何优化一个老项目的webpack打包构建经历,向小伙伴们介绍了happypackthread-loader以及terser-webpack-plugin的用法。希望能够给小伙伴们在webpack的构建中带来一些帮助,也希望小伙伴们可以从中得到一些启发~