webpack5升级记录
最近有空把公司的项目升级到webpack5,顺手记录下一些改造的流程。
三方包升级
第一件事就是把三方包里涉及到webpack的基建工具升级,主要可以分两类,一类是webpack本身构建流程需要的工具,比如webpack,webpack-cli,webpack-merge等等,这种无脑升到最新就可以,另外一类是相关的loader和插件,这种需要关注下最新版本的版本号和更新时间,版本号基本没变化的或者很久不更新的大概率是在webpack5有更好的替代方案了,可以先记录在后续改配置的时候注意。
批量升级用npm-check-updates这个工具就可以。
解决配置报错
升级包后下一步首先就是解决开发起server和打包时的报错问题了,先让这两个流程走通。基本上就是跑起来看控制台报什么错一个个解决就可以了。可以先参考webpack给的升级文档里面的一些字段迁移先改掉。这里我总结了在我们项目中这一步改掉的报错配置内容。
webpack-merge解构引⼊
splitChunks.cacheGroups.vendors => splitChunks.cacheGroups.defaultVendors
sourceMap类型命名变动:cheap-module-eval-source-map => cheap-module-source-map
resolve配置新增fallback写法适配
fallback: { crypto: require.resolve("crypto-browserify"), stream: require.resolve("stream-browserify") }
less-loader配置改动
// 旧 { loader: require.resolve('less-loader'), options: { javascriptEnabled: true } } // 新 { loader: require.resolve('less-loader'), options: { lessOptions: { javascriptEnabled: true } } }
插件写法变更
// IgnorePlugin 旧 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) // IgnorePlugin 新 new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }) // CopyWebpackPlugin 旧 new CopyWebpackPlugin([ { from: path.resolve(context, 'public/js'), to: `${distSourceFolderName}/js`, ignore: ['.*'] } ]), new CopyWebpackPlugin([ { from: path.resolve(context, 'public/img'), to: `${distSourceFolderName}/img`, ignore: ['.*'] } ]) // CopyWebpackPlugin 新 new CopyWebpackPlugin({ patterns: [ { from: path.resolve(context, 'public/js'), to: `${distSourceFolderName}/js`, globOptions: { ignore: ['.*'] } }, { from: path.resolve(context, 'public/img'), to: `${distSourceFolderName}/img`, globOptions: { ignore: ['.*'] } } ] }) // DefinePlugin 旧 new webpack.DefinePlugin({ PRODUCTION: JSON.stringify(!devMode) }), // DefinePlugin 新 new webpack.DefinePlugin({ 'process.env': { PRODUCTION: JSON.stringify(!devMode) } })
过时的loader和plugin替换
- cache-loader => 持久化缓存
- awesome-typescript-loader => ts-loader
- file-loader, url-loader, raw-loader => 官⽅⽀持的asset配置
- eslint-loader => EslintPlugin
- clean-webpack-plugin => clean: true配置
- optimize-css-assets-webpack-plugin => css-minimizer-webpack-plugin
一些命名和写法上的变动我就不细讲了,主要讲几个关键的内容。
首先是resolve的fallback配置,webpack5和4的一个区别就是4会自动polyfill Node的核心模块,5把这部分隐式的依赖做了分离,所以项目中用到的Node相关模块在5中就要单独引用对应polyfill包。
cache-loader已经不维护了,因为webpack5内置了持久化缓存这个新特性可以替代这个loader,使用cache这个字段配置,主要作用就是缓存已经生成过的webpack模块和chunk改善开发环境下的重构建速度。type有两种配置,默认是内存,设置成文件系统才可以开启更多的配置。version这个就是版本控制字段,不同版本的缓存不共用,一般用webpack的一些环境变量hash后区分版本。store用来控制将缓存存放到文件系统的时机,现在只有pack这一个配置,表示当编译器闲置时存放。buildDependencies表示缓存依赖的文件,这里变动后缓存就会失效。
asset配置也是webpack5新增的配置,可以理解就是把原来的资源loader进行了一个官方整合,包括资源输出到指定目录,编码后内嵌dataUrl等功能。
clean-webpack-plugin原来是结合打包脚本一起用的,每次打包清除上次的产物,这个现在官方也直接提供clean配置实现了。
到这一步webpack5已经可以正常使用了。下面就可以利用webpack5的新特性做一些进一步的优化,顺便把原来本身不太合理的配置也优化一下。
旧配置优化
- 产物hash改为contenthash
首先是可以把涉及到产出hash的部分都改成contentHash,这个配置有hash, chunkhash和contenthash 3种,hash就是每次打包对应一个hash,chunkhash就是每次打包根据依赖分出的不同chunk关系去hash,每块chunk的依赖不变这个值就不会变,contenthash就是产出内容不变这个值就不会变,目前打包产出的最佳实践都用contenthash就行。
- 移除pathInfo
pathinfo这个配置决定是否在产出的bundle中引入所包含module信息的注释,最佳实践是开发环境展示生产环境不展示,移除用webpack提供的默认配置就可以了。
- stats使用预设
stats控制打包产出的bundle信息如何展示,这个值官方有提供一些预设,一般用错误或有警告的bundle信息输出预设就行。
- oneOf匹配
oneOf匹配指的是配置rules规则时已知每个资源不会串行处理下就要声明oneOf,因为webpack会默认把每个资源都用配置的loader去串行匹配一遍,oneOf声明后匹配到其中一个后就不会继续串行处理了,可以提高一些资源处理效率。
- 引⼊CaseSensitivePathsPlugin⽀持⼤⼩写敏感区分
CaseSensitivePathsPlugin这个插件解决mac系统下大小写不敏感的问题,相信有的同学应该遇到过本地build正常,线上流水线环境报错,因为引入文件大小写不对的问题,因为mac系统大小写不敏感,Linux系统大小写敏感,这个插件就是解决这个问题。
- 引⼊react-refresh热刷新⽅案
This package implements the wiring necessary to integrate Fast Refresh into bundlers. Fast Refresh is a feature that lets you edit React components in a running application without losing their state. It is similar to an old feature known as "hot reloading", but Fast Refresh is more reliable and officially supported by React.
这个和webpack官方提供热加载比较类似,不过相比于webpack这个react热刷新是专门针对react项目做的方案,也是官方支持的,对于react项目应该效果会更好一些。引入也很简单,在开发插件和babel编译的地方引入对应插件即可。
待考察
最后再讲下几个待考察的在公司项目中可能收益大过风险的优化方案。
- esbuild-loader替换babel
用esbuild-loader替代babel编译,虽然可以提升编译速度,不过esbuild不能用babel的插件,项目中可能有一些依赖babel的三方包会出问题,比如基于antd实现的那一套旧组件和hooks。
- 移除dll 和splitChunks配置
现在最佳实践一般都不要手动去配置dll和splitChunks去控制缓存,不过我试了下,因为我们项目中依赖的旧组件和hooks问题,移除手动配置的splitChunks后入口文件会变得很大,单独移除dll后三方包加载顺序在手动splitChunks的影响下也有一些异常导致启动报错。目前看splitChunks和dll共同作用对加载顺序影响还比较大,这两块也先不动比较好。
- serviceWorker缓存配置
serviceWorker缓存现在也有比较简单的方法可以配,不过缓存强度比较高,配置出错可能会出现旧代码更新后缓存还没失效的问题,这块还要有比较长的测试周期下再改。
小结
可以看到升级过程中的流程和大部分通用改动还是可以参考的,不过部分配置优化也要参考项目本身的现状。
-- EOF --