前言
webpack5 现在的最新版本已经是5.5了,相对来说比较稳定,但是截止到2021.08.19,React的相关脚手架工具create-react-app最近一次更新仅仅是在14天前,commit message 是 :『webpack5』。
所以即使webpack5本身比较稳定了,但主流框架的一些工具也可能最近才做了适配或者还没有做适配,也就是说本文的一些问题不一定具有长效性,在你升级的过程中可能不会遇到本文提到的问题,也可能遇到本文没有提到的问题。
综上本文仅供参考。
前期调研
Node的版本要在10.13.0及以上- 如果你是
webpack3,请先将他升级到webpack4
准备工作
首先打开webpack给出的官方升级指南:
webpack: to V5 from V4: webpack.js.org/migrate/5/
可以发现里头已然讲的非常具体了,我们首先要做以下几步:
1. 首先将webpack4升级到version 4以下的最新版本,目前最新的版本是4.46.0
2. 升级所有的loader以及plugin到适配webpack5的版本
这里要注意的是有些loader以及plugin的@latest版本并不是适配webpack5的版本,而@next版本才是,所以升级的时候建议核对一下该loader/plugin的npm首页,查看是否有@next的版本,并安装正确的版本.
!注意: 当你的npm版本在7以上的时候,在安装最新的loader/plugin过程中很有可能会报出如下错误:
解决方法在报错中也给出了,在安装命令后应加上--legacy-peer-deps,所以你的安装命令应该如下:
npm i ${yourPluginName@yourPluginVersion} --legacy-peer-deps
出现这个错误的原因是npm在7以上的版本中,默认安装peerDependencies
插播:简单介绍一下peerDependencies:
我们常见的是dependencies和devDependencies,前者是依赖,后者是开发时安装的依赖,而peerDependencies(如下图所示)相对来说比较陌生
我们可以用一个例子来简单了解一下peerDependencies,如下:
如果dependency1和dependency2都在Dependency里声明了dependency3,并且依赖的版本不同,那么他们的依赖树应该是这样:
root/node_modules/
|
+- dependency1/node_modules/
| |
| +- dependency3 v1.0/
|
|
+- dependency2/node_modules/
|
+- dependency3 v2.0/
这样项目会安装两个版本的dependency3。然而,如果声明在了peerDependencies里,依赖树将会是这样:
root/node_modules/
|
+- dependency1/
|
+- dependency2/
|
+- dependency3 v1.0/
也就是说,peerDependencies里所需要的依赖会统一安装在根目录下,而不会安装多次
所以,回到上面那副图,你会发现peerDependencies里依赖的版本往往是一个范围,而不是某个特定的版本号
npm在7以前的版本不会首先根据peerDependencies安装依赖,所以不会报错,而7以上的版本会则会抛出错误
在升级loader/plugin过程中,这两者一般都是基于webpack开发的,与根目录webpack版本不兼容,所以抛出这个错误(例如最新的plugin要求的webpack版本是"^5.0.0",而咱们根目录的是"4.x")
加入--legacy-peer-deps后就告诉npm每个module安装各自版本的第三方包,安装可以顺利进行,等升级完成后,我们再重新npm install一遍,也可以减少依赖的下载和安装
3. 如果使用了webpack-cli将其升级到最新版本
4. build 你的项目,尽量保证没有错误报出 (此时版本还是4.x)
5. 如果使用了以下配置或plugin,更改其写法
optimization.hashedModuleIds: true→optimization.moduleIds: 'hashed'optimization.namedChunks: true→optimization.chunkIds: 'named'optimization.namedModules: true→optimization.moduleIds: 'named'NamedModulesPlugin→optimization.moduleIds: 'named'NamedChunksPlugin→optimization.chunkIds: 'named'HashedModuleIdsPlugin→optimization.moduleIds: 'hashed'optimization.noEmitOnErrors: false→optimization.emitOnErrors: trueoptimization.occurrenceOrder: true→optimization: { chunkIds: 'total-size', moduleIds: 'size' }optimization.splitChunks.cacheGroups.vendors→optimization.splitChunks.cacheGroups.defaultVendorsCompilation.entries→Compilation.entryDependenciesserve→serve被DevServer替代 (一般会安装webpack-dev-server,所以这个一般不用改)Rule.query(webpack3以来就弃用了) →Rule.options/UseEntry.options
安装webpack5
npm install webpack@latest
(可能你还是要用到 --legacy-peer-deps 命令)
生态对齐+踩坑记录
1. webpack-dev-server需要升级
webpack-dev-server需要升级到4.x版本,也就是@next版本,虽然其gitHub的更新记录表明在3.10以上的版本就已经做了webpack5的兼容,但是在我的项目里并不是适用。各位可以自己试一下,如果3.10可以跑起来就最好,但如果要升级到4.x版本,就要注意,配置项写法有很多更改,具体详情请看:
2. create-react-app 相关报错
TypeError: message.split is not a function. in node_modules\react-dev-utils\formatWebpackMessages.js
这个error存在了很久了,具体可参考这个链接:
React官方直到14天前的那一次把脚手架更新到next版本才修复了这个问题,所以安装了最新的react-dev-utils(再次提醒,是@next版本)就可以解决这个问题,然而你会碰到另外的问题,如下:
can not resolve react-dev-utils/WatchMissingNodeModulesPlugin以及can not resolve react-dev-utils/typescriptFormatter
原因也很简单,打开react-dev-utils,最新版本的react-dev-utils删除了这俩函数(typescriptFormatter可能是在更早的版本删除的)
解决办法就是将两者移除即可,这两者并没有改名,最新版本react-dev-utils要么将这些移除的函数功能内嵌进了其他函数,要么就是被其他loader替代,要么就是webpack5自带了这些功能
3. 更改相关webpack.config.js的node: {}
如果你的webpack.config.js有类似代码,你需要将他们移到resolve.fallback
before
node: {
fs: 'empty',
},
after
resolve: {
fallback: false,
},
4. webpack5取消了polyfill
这是一个比较好解决的问题,当你跑项目的时候webpack5会做出对应的提示,同样,你需要在resolve.fallback里添加相关代码,类似这样:
fallback: {
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"zlib": require.resolve("browserify-zlib"),
"assert": require.resolve("assert/"),
"buffer": require.resolve("buffer/"),
"stream": require.resolve("stream-browserify"),
"os": require.resolve("os-browserify/browser")
}
添加完成后再把所需的第三方包自行安装一下即可
5. 更换optimize-css-assets-webpack-plugin
在optimize-css-assets-webpack-plugin的gitHub首页明确指出在webpack5以后推荐使用官方的css-minimizer-webpack-plugin
webpack5新特性
1. 自带cache
在webpack5以前,做cache可能需要的是第三方的loader对开销比较大的模块做缓存,但是到了webpack5,其自带了cache,用户可以通过如下简单的配置来做缓存:
// webpack.config.js\
module.exports = {
...,
cache: {
type: 'filesystem',
// 可选
buildDependencies: {
config: [__filename], // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效(官方建议为[__filename]即可,可以及时更新相关依赖)
},
name: '', // 可以配置以name为隔离,不同的判断条件输出不同的缓存文件,如生成PC或mobile不同的配置缓存
...,
},
}
注意,这里只有type是必填的,有两个选项memory和filesystem,如果使用了memory,其他的cache配置选项将会失效。以上只是cache的某些配置,全部的详情请看
-
⚠️注意⚠️
很多项目使用
webpack是通过node模式,也就是会有compilerconst compiler = webpack(config);请检查你的
bulid.js文件,如果是使用了compiler,务必加上compiler.close(() => {}),不无法正确输出:(github.com/webpack/web…)
2. 内置静态资源构建能力 —— Asset Modules
- raw-loader:允许将文件处理成一个字符串导入
- file-loader:将文件打包导到输出目录,并在 import 的时候返回一个文件的 URI
- url-loader:当文件大小达到一定要求的时候,可以将其处理成 base64 的 URIS ,内置 file-loader
由于webpack只打包js文件,所以在使用过程中,我们常常用以上几个loader来处理静态资源,然而在webpack5里同样自带了这些功能,大致如下:
module: {
rules: [
// ...
{
resourceQuery: /raw/,
type: 'asset/source',
}
]
},
更改type代表不同的loader,详情可看 webpack.js.org/guides/asse… 以及 dev.to/smelukov/we…
webpack5还有很多新功能,例如支持Top Level Async,内置web worker等等,但这些与升级过程有关,我的项目中也没有用到,就不展开说了,希望各位升级顺利。