性能优化
构建优化
你是如何优化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只分割异步模块
}
}