基于react和typescript的webpack5 升级参考-2021.8.19

1,856 阅读6分钟

前言

webpack5 现在的最新版本已经是5.5了,相对来说比较稳定,但是截止到2021.08.19,React的相关脚手架工具create-react-app最近一次更新仅仅是在14天前,commit message 是 :『webpack5』。

所以即使webpack5本身比较稳定了,但主流框架的一些工具也可能最近才做了适配或者还没有做适配,也就是说本文的一些问题不一定具有长效性,在你升级的过程中可能不会遇到本文提到的问题,也可能遇到本文没有提到的问题。

综上本文仅供参考。

前期调研

  1. Node 的版本要在10.13.0及以上
  2. 如果你是webpack3,请先将他升级到webpack4

准备工作

首先打开webpack给出的官方升级指南:

webpack: to V5 from V4: webpack.js.org/migrate/5/

可以发现里头已然讲的非常具体了,我们首先要做以下几步:

1. 首先将webpack4升级到version 4以下的最新版本,目前最新的版本是4.46.0

image.png

2. 升级所有的loader以及plugin适配webpack5的版本

这里要注意的是有些loader以及plugin@latest版本并不是适配webpack5的版本,而@next版本才是,所以升级的时候建议核对一下该loader/pluginnpm首页,查看是否有@next的版本,并安装正确的版本.

!注意: 当你的npm版本在7以上的时候,在安装最新的loader/plugin过程中很有可能会报出如下错误:

image.png

解决方法在报错中也给出了,在安装命令后应加上--legacy-peer-deps,所以你的安装命令应该如下:

npm i ${yourPluginName@yourPluginVersion} --legacy-peer-deps

出现这个错误的原因是npm7以上的版本中,默认安装peerDependencies


插播:简单介绍一下peerDependencies

我们常见的是dependenciesdevDependencies,前者是依赖,后者是开发时安装的依赖,而peerDependencies(如下图所示)相对来说比较陌生

image.png

我们可以用一个例子来简单了解一下peerDependencies,如下:

如果dependency1dependency2都在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里依赖的版本往往是一个范围,而不是某个特定的版本号


npm7以前的版本不会首先根据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: true
  • optimization.occurrenceOrder: true → optimization: { chunkIds: 'total-size', moduleIds: 'size' }
  • optimization.splitChunks.cacheGroups.vendors → optimization.splitChunks.cacheGroups.defaultVendors
  • Compilation.entries → Compilation.entryDependencies
  • serve → 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版本,就要注意,配置项写法有很多更改,具体详情请看:

github.com/webpack/web…

2. create-react-app 相关报错

  • TypeError: message.split is not a function. in node_modules\react-dev-utils\formatWebpackMessages.js

这个error存在了很久了,具体可参考这个链接:

github.com/facebook/cr…

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.jsnode: {}

如果你的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

image.png

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是必填的,有两个选项memoryfilesystem,如果使用了memory,其他的cache配置选项将会失效。以上只是cache的某些配置,全部的详情请看

webpack.js.org/configurati…

  • ⚠️注意⚠️

    很多项目使用webpack是通过node模式,也就是会有compiler

    const 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等等,但这些与升级过程有关,我的项目中也没有用到,就不展开说了,希望各位升级顺利。