lucifer

最近将项目的 webpack 4 升级到了 webpack 5,用了一两天的时间,遇到了一些网上找不到资料的问题, 于是决定将过程记录一下。

直接找到官方提供的迁移教程 To v5 from v4 进行操作。

升级 webpack

将项目依赖的版本直接改为最新版本,注意将变更同步到 package.json 和 lock 文件。

逐个检查 loader 和 plugin 的 兼容性

我用到了一个叫 virtual-module-webpack-plugin 的插件,根据 readme 的介绍,直接改为 webpack-virtual-modules 即可。

写法有一点小小的变动。

before:

1
2
3
4
5
6
plugins: [
new VirtualModulePlugin({
moduleName: "src/mysettings.json",
contents: JSON.stringify({ greeting: "Hello!" }),
}),
];

after:

1
2
3
4
5
plugins: [
new VirtualModulesPlugin({
"src/mysettings.json": JSON.stringify({ greeting: "Hello!" }),
}),
];

检查项目中是否使用到了 breakchange 功能

使用官方提供的命令在项目下跑一下即可。

1
node --trace-deprecation node_modules/webpack/bin/webpack.js

核心就是在项目下跑一下 webpack,webpack 会打印一些 deprecation 警告,通过上面的命令可以将其筛出来。

  1. 内置模块不再 polyfill

由于 webpack 5 内置不在 polyfill path,process 等,因此如果你的项目用到了,需要自己处理。处理方式也很简单。

对于 path:

1
config.resolve.fallback.path = require.resolve("path-browserify");

更多请参考:how-to-polyfill-node-core-modules-in-webpack-5

  1. 个别 sourcemap 选项变更

比如 cheap-module-eval-sourcemap 改为了 eval-cheap-module-source-map

这个问题虽然网上我没找到解决方案,但是我问了 chatgpt 它告诉了怎么做(感谢chatgpt)。

  1. 如果你开发了一些插件和 loader, 那么要注意一些 api 变了。

比如 assets.compilation,再或者 compiler.hooks.invalid.call

这些变化虽然没有在官方文档体现,但是你的项目中如果使用 ts,那么其会直接报错,应该也不算太坑。

循环依赖

webpack4 默认帮助我们处理循环依赖问题。 而webpack5则不会。

webpack5 会报错:webpack 5 cannot access '__webpack_default_export__' before initialization

因此如果你的项目中使用了循环依赖, 可以尝试改变写法。如果你不知道哪里有循环依赖,也可以使用 webpack 官方推荐的检测插件。

Circular Dependency Plugin - Detect modules with circular dependencies when bundling — Maintainer: Aaron Ackerman

types 丢失

webpack 4 的版本,如果直接使用 api 模式, 那么可以使用很多它导出的类型,webpack 5 中不再导出, 可以使用 patch-package 的方式修改包,也可以自己 declare webpack module 去扩展。

比如不再导出的类型有:

  • MultiWatching
  • LoaderContext
  • Plugin

升级到 swc-loader

之前用的是 babel-loader,这次顺便升级一下 swc,速度据说比 babel-loader 快很多。实际使用我的项目要快 20%-40%。由于每个项目不一样,仅供参考。

升级 swc-loader 很简单。

before:

1
2
3
4
5
6
7
8
9
10
11
12
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules)/,
use: {
// `.babelrc` can be used to configure swc
loader: "babel-loader",
},
},
];
}

after:

1
2
3
4
5
6
7
8
9
10
11
12
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules)/,
use: {
// `.swcrc` can be used to configure swc
loader: "swc-loader",
},
},
];
}

由于我项目中使用了 sourcemap,因此直接修改会报错。

1
swc-loader 0: failed to read input source map from user-provided sourcemap

解决方式很简单,直接 swc-loader 传递 sourceMap: false 即可,这样swc-loader 就不处理 sourcemap 了。如果 为 true 的话,swc-loader 会将 sourcemap JSON.stringify 一下,从而出问题。

swc-loader 处理这部分的源码大概是:

1
swc.transform(source, options).then(output => callback(null, output.code, parseMap ? JSON.parse(output.map) : output.map), err => callback(err))

也就是说设置不解析 sourcemap 后就不会序列化了。

其他遇到的问题

  • node.setImmediate 不支持了,其实 node 上的东西都不支持了。比如 buffer, path 等等。
  • optimization.noEmitOnErrors 变为了 optimization.emitOnErrors,这个就是 true 和 false 换一下的事,不理解 webpack5 为啥要改这个。
  • compiler.outputFileSyste.write 签名好像变了
  • compilation.assets 的类型变了。之前 Source 类的 map 函数返回值可以是 null, 现在只能是 Object 类型

这几个解决方式网上都有,也很简单,不再赘述。

总结

这次迁移使得项目性能提升 20% - 30%,总共花了一两天的时间,效果还不错。

另外也会之后升级 rspack 做了准备,因此 rspack 给的迁移例子都是 webpack 5 的, 也就是说如果 webpack 5 出问题更容易找到资料。

我自己试了一下, 目前强行迁移到 rspack 坑很多,并且网上几乎没资料可以参考,所以决定先等等等到社区相对成熟了再入坑。


 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 。
载入天数...载入时分秒...