记一次 webpack 4 升 5 的心路历程

3,769 阅读3分钟

前言

由于最近在使用 qiankun 搞微前端,遇到个优化问题即假如基座和子 app 都共用了同一版本的 vue 和 vantUI,那么就导致说基座和主应用都会分别打包一份,导致了用户需要请求的代码体积增加,为了优化这个问题一开始是想到 webpackexternalqiankun 自带的通过 props 传递给子应用,但是相对来讲都不是最优方案,最优方案就是用 module federation,所以就要升级到 webpack5

简单项目升级 webpack 5

先用基座项目试试,因为基座项目比较简单,没有太多的 loader 和 plugin 首先根据官网的升级文档执行

Yarn: yarn add webpack@next -D

看到这个 @next,感觉应该有坑,因为有可能作者忘记把 next 这个版本更新到最新版本,果然安装出来之后是 ^5.0.0-rc.6 版本,看到 rc 这个标识不详的预感更强烈,因为 rc 是 release candidate 的意思,预发布的候选版本,果然用 npm view webpack version 看了下最新是 5.3.1,先不管了,先 start 项目试试。

果然报了个错,看了下语义说的是 htmlWebpackPlugin 下某个对象被冻结了不能添加属性,那么应该是 htmlWebpackPlugin 的版本过低也需要升级,用 npm outdated 看了下项目内有没有过老的版本,连带一起升级了。发现 htmlWebpackPlugin 没有在内,那么推算应该是 htmlWebpackPlugin 应该是没有适配 webpack5 的原因。

Cannot add property htmlWebpackPluginAlterChunks, object is not extensible

通过看了下 htmlWebpackPlugin 的最新版本也是 5.3.1 那么跟 webpack 版本对的上了,那么将 webpack 和 htmlWebpackPlugin 都一起升级到 5.3.1,启动项目,问题解决。

升级稍复杂的 vue cli 项目

这次的升级比之前要大很多,主要还是因为 vue cli 对 webpack5 的支持才是 beta 版本,并且没有比较详细的迁移文档,网络上资料也比较少。

首先把 @vue/cli 主要 3 个使用到的工具升级到最新:

"@vue/cli-plugin-babel": "^5.0.0-beta.1",
"@vue/cli-plugin-eslint": "^5.0.0-beta.1",
"@vue/cli-service": "^5.0.0-beta.1",

启动,报错:

jsonpFunction not available in webpack 5

查阅资料,发现是 jsonpFunction 配置在 webpack 中换成了 chunkLoadingGlobal。 继续启动,报错:

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
        - add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
        - install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
        resolve.fallback: { "crypto": false }

根据提示可知之前 webpack 针对部分包中引入 node 的模块做了兼容处理,但是 webpack 5 已经放弃了对这些 node 包的默认兼容,所以有两种建议,一种是直接用空包代替兼容包,即:

resolve.fallback: { "crypto": false }

正确的应该是这样配置,但是又碰到了 vue cli 还是在 beta 版本,在 chainWebpack 内配置 config.resolve.fallback 是无效的,大概率是还没有支持的原因,回头去看看,是的话提个 PR。

目前暂时的解决办法就是使用 alias,因为 vue cli 暂无 fallback 配置。

config.resolve.alias.set('crypto', require.resolve("crypto-browserify"))

再重新启动:

Syntax Error: ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'plugins'. These properties are valid:
   object { postcssOptions?, execute?, sourceMap?, implementation? }

通过参考我目前的配置:

css: {
loaderOptions: {
  sass: {
    // 根据自己样式文件的位置调整
    prependData: '@import "@/mixin.scss";'
  },
  postcss: {
    plugins: [
      require('postcss-px-to-viewport')({
    viewportWidth: 750,
    viewportHeight: 1334,
    unitPrecision: 3,
    viewportUnit: 'vw',
    selectorBlackList: ['.ignore', '.hairlines', /^\.dp/, /^\.scroller/, /^\.van/],
    minPixelValue: 1,
    mediaQuery: false
  }),

根据提示,应该是配置的结构或者配置名改变了,并且因为 vue cli 还在 beta 阶段,这个 google 也找不到答案,最后根据意思试了几次。发现最后的结构是:

loaderOptions: {
      sass: {
        // 根据自己样式文件的位置调整
        prependData: '@import "@/mixin.scss";'
      },
      postcss: {
        postcssOptions: {
          plugins: [
            require('postcss-px-to-viewport')({
              viewportWidth: 750,
              viewportHeight: 1334,
              unitPrecision: 3,
              viewportUnit: 'vw',
              selectorBlackList: ['.ignore', '.hairlines', /^\.dp/, /^\.scroller/, /^\.van/],
              minPixelValue: 1,
              mediaQuery: false
            }),

继续启动报错:

Eslint not a controller

将 Eslint 升级到最新即可。

继续启动报错:

 3:26  error  Parsing error: Unexpected token

  1 | <template>
  2 |     <div>

通过报错信息得知是 eslint 的解析报错,查阅资料通过以下方式解决:

- "parser": "babel-eslint",
+ "parser": "vue-eslint-parser",
  "parserOptions": {
+     "parser": "babel-eslint",
      "sourceType": "module"
  }

最后启动项目成功。

总结:

由于 vue cli 是将 webpack 配置封装过的,假如在目前升级文档还没有出来的时候去升级,阻力还是挺大的,大部分需要靠对错误提示的理解和推测去解决,部分是在网上找不到答案的,需要自己摸着石头过河。也希望有一样经历的同学可以在这个文章中少走弯路。