干货!我是如何在一线大厂实践webpack优化的

10,810 阅读6分钟

1 前言

大家好,我是心锁,一枚23届准毕业生。

这次的文章主题是「webpack」,将叙述我在某一线大厂的某项目中进行前端工程化的实践,前方高能预警⚠️

阅读本文,你将会了解到

  • Webpack4->Webpack5升级指南
  • Webpack优化实战

值得注意的是,这次我们只讲实践,不入原理

1020055674B596C2B83948ADE0679D33

2 webpack升级实践

2.1 升级的目的

webpack5带来了几个非常管用的新特性,包括

  • 开箱即用的持久化缓存
  • 优雅的资源处理模块
  • 打包体积优化

前两个特性在我们的项目中的适用场景相对较广,而打包体积优化这一项则是前端工程化喜闻乐见的

0E9417AA32C9237EE375B432C56D18F4

2.2 升级的变化

1C3AD946FD48909CC2EC9A8FA956A318

2.2.1 命令行env的传参格式变化

  • 错误示范

1C3AD946FD48909CC2EC9A8FA956A318

  • 正确操作

Webpack5不再需要使用--env.key=value的语法,现在使用--env key=value

2.2.2 webpack-dev-server命令调用方式变化

  • webpack-dev-server config.js->webpack server config.js

出于兼容考虑,我们需把webpack-dev-server升级到最新版本

npm install -D webpack-dev-server@latest

2.2.3 资源加载配置方式的变化

webpack5之前,通过url-loaderfile-loader等loader来决定一些静态资源的加载。

而现在我们只需要指定type:

  • asset/resource会将对应的资源加载成url,对应以前的file-loader
  • asset/inline导出一个资源的data URL,对应以前的url-loader
  • asset/source,对应以前的 raw-loader
  • asset,可以在这里通过限制limit 来决定使用inline还是resource

image-20220808181402045

2.2.4 核心包依赖版本升级

"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3",

webpack升级到5.x的情况下,至少需要把这些webpack相关的依赖包更新到最新版本(我这里都是通过@latest更新)

npm install webpack@latest webpack-cli@latest webpack-dev-server@latest

2.2.5 需要手动注入Node polyfill

依据官方的说法,webpack5之后不再默认为工程注入NodeJS polyfill,即如果你在webpack4版本的代码中使用了类似process这样的Node变量,需要手动安装依赖与配置fallback

当然这是一个好事情,因为不需要注入所有垫片,可以减少构建体积

48C0DB306922DBD18A546DAD5C9A5920

#1 依赖对应

检查下表,这是webpack官方给出webpack4使用的polyfill包

image-20220808183929602

#2 通过fallback注入垫片

image-20220808184053872

#3 代码的改变

process的使用在webpack4是无需导入的,但是在webpack5这里我们最好手动导入

image-20220808235253704

2.2.6 替换或者升级不兼容webpack5的插件

举个例子,该项目中使用到了webpack-cos-plugin插件进行生产环境下编包自动上传,然而遗憾的是webpack-cos-plugin最新版也只支持到webpack4,在这种情况下我们只能替换插件。

幸运的是社区有同学开发了webpack5-cos-plugin,完全兼容webpack-cos-plugin

645663A9ED81C1B1A1F4BE8415352CC8

3 webpack优化实战

3.1 构建时间优化

构建时间越短,整体开发体验越好。

我们在开发环境引入了speed-measure-webpack-plugin用来测速

image-20220808203019425

image-20220808203048366

3.1.1 持久化缓存

image-20220808193733459

开启持久化缓存可以大幅降低我们项目二次构建的时间,非常建议开启,建议使用filesystem,使用memory对于中大型项目会吃不消

3.1.2 sourcemap优化

使用webpack打包文件的时候,可以选择生成sourcemap文件。

按照社区推荐

  • 建议在开发环境使用eval-cheap-module-source-map,内联sourcemap,减少构建时间。

image.png

  • 建议在生产环境使用source-map,生成专门的.map.js文件,一般来讲根据具体需求删除或者移动sourcemap文件,增加代码被反编译的难度

image.png

3.1.3 watch优化

大部分项目中,node_modules的变换频率都是极低的,所以我们在使用watch功能的时候可以通过配置

ignored来忽略node_modules从而减少性能压力

image-20220808200918320

3.1.4 在开发环境中使用style-loader

之前的QAPM项目中,不管是开发环境还是生产环境一开始都是通过MiniCssExtractPlugin生成css文件并引入页面的方式来使用css

image-20220808201238517

然而这是不对的,一方面MiniCssExtractPlugin并对于热更新HMR支持的不是很好

(这里还有一个问题,开发环境配置hash会使得构建性能进一步下降)

我这里的解决方案是在开发环境中使用style-loader,这个loader作为webpack的入门级loader天然支持HMR并且简单易用,同时性能上也相对比较好。

image-20220808201013487

3.1.5 TerserPlugin插件缓存

这个其实没啥好说的,记得把缓存打开,同时最好设定一定的exclude,比如去除node_modules

image.png

3.1.6 noParse优化

React已经为我们打包了生产环境需要使用的文件,这样可以跳过编译环节

3.1.7 Js加载优化

这里的优化点总结如下

  • 使用thread-loader加快构建速度
  • 为babel-loader开启缓存
  • 通过exclude排除node_module
  • 使用react-refresh/babel为React项目添加热更新能力

image-20220808202746789

3.2 打包体积优化

我们在生产环境构建的config文件中使用webpack-bundle-analyzer来分析打包体积

image-20220808203125162

image-20220808203437098

3.2.1 lodash优化

由于lodash是一个UMD规范的包,所以默认做的全量引入

image-20220808204433162

image-20220808203732889

我们可以通过LodashModuleReplacementPlugin来移除你未用到的lodash特性

3.2.2 moment优化

webpack 打包momentjs时会把所有语言包都打包,这样会使打包文件很大。此插件可以帮助我们只打包需··要的语言包,大大减小打包文件大小。

image-20220808210003575

限定查找 moment/locale 上下文里符合 /zh-cn|en-gb/ 表达式的文件,只打包这几种本地化内容

3.2.3 CSS tree-shaking

通过PurgeCSS来进行CSS体积优化,CSS的作用原理是通过正则,所以建议使用PurgeCSS时一定要配置好白名单,同时保证选中所有的使用到样式文件类的地方。否则可能会丢失样式

image.png

3.2.4 splitChunks提取公共代码

SplitChunks插件是webpack中用来提取或分离代码的插件,主要作用是提取公共代码,减少代码被重复打包,拆分过大的js文件,合并零散的js文件

image.png

在webpack5中使用,我们可以根据实际情况进行拆包,从而减少构建体积。(一般拆一下node_modules)

对于这里的规则,将只说只说一些重点

  • minChunks:引用阈值,被引用次数超过该阈值的模块才会被拆包处理;
  • maxInitialRequest/maxAsyncRequests:用于限制Initial/Async Chunk最大并行请求数,本质上是在限制最终产生的分包数量;
  • minSize: 超过这个大小的 Chunk 会被拆包;
  • maxSize: 超过这个大小的 Chunk 会尝试继续拆包;
  • cacheGroups:缓存组规则,为不同类型的资源设置更有针对性的拆包策略
  • priority: 优先级,在缓存组规则中使用

3.2.5 gzip压缩

配置compression-webpack-plugin插件,生成额外的gzip静态文件

image.png

这种形式对于我们使用nginx的项目只需要简单开启gzip_static和gzip_proxied

image.png

如果使用CDN的话就要看服务商有没有提供相关功能

4 总结

webpack优化走一圈下来,其实准则很简单,无非「最小化约束」「持久化赋能」「分化生产开发」

244392FC225E2177F8435874B3A49BE3

说人话就是多用exclude约束作用范围,多使用缓存提升二次构建性能,区分生产环境与开发环境分化不同的需要。

DFAF79D6BD2D7BF42483CD989B41A0C7

最终输出一份对比报告

image-20220808195138495