webpack4升级到webpack5的实战经验总结

目录
  • 前言
  • terser-webpack-plugin语法报错
  • fork-ts-checker-webpack-plugin语法报错
  • IgnorePlugin报错
  • devtool报错
  • webpack-dev-server publicPath报错
  • webpack-dev-server disableHostCheck报错
  • 移除 node.js polyfill
  • 导入json文件语法改变
  • 打包报错unknown option ‘-p’
  • node版本过低
  • 图片编译问题
  • 打包文件命名问题
  • 废弃了ModuleConcatenationPlugin
  • 升级前后对比
  • 总结

前言

最近接手了公司内部webpack相关的依赖包,于是打算优化一波。考虑到webpack5已经正式发布近两年,跟webpack相关的依赖包应该适配的差不多了,于是打算先把webpack4升级到webpack5,然后基于webpack5再进行优化。

升级前用的是 "webpack": "^4.42.1" ,升级后用的是 "webpack": "^5.72.1"

笔者采用的升级webpack的方法是先一键升级所有的依赖包,然后一个一个地去解决运行过程中的报错。

首先,全局安装npm-check-updates:

yarn global add npm-check-updates

然后在项目中执行 ncu -u ,这样项目的package.json会把所有的依赖包都更新到最新版本,然后执行 yarn 。升级完就可以开启漫长的debug之旅了。

terser-webpack-plugin语法报错

Invalid options object. Terser Plugin has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'cache'. These properties are valid:
   object { test?, include?, exclude?, terserOptions?, extractComments?, parallel?, minify? }

原来的配置:

minimizer: [
	new TerserPlugin({
		cache: true,
		parallel: true,
		terserOptions: {
			mangle: false, // Note `mangle.properties` is `false` by default.
		},
	}),
]

从报错来看,terser-webpack-plugin的配置属性发生了改变,现在已经没有 cache 这个选项了。

terser-webpack-plugin是用来压缩JavaScript代码的。不过webpack5已经自带了terser-webpack-plugin,如果说你用的是webpack5或者更高版本,同时还希望自定义配置,那么还是需要安装terser-webpack-plugin。

webpack4的时候可以通过terser-webpack-plugin的cache属性开启文件缓存。现在webpack5自身提供了持久化缓存机制,它能够将首次打包的结果缓存到硬盘中,等下次打包的时候就可以跳过一系类的耗时的操作,复用第一次的打包结果。可以通过以下配置开启持久化缓存:

cache: {
 type: 'filesystem',
 version: 'yourVersion'
}

缓存默认保存路径是 node_modules/.cache/webpack。这里要注意每当我们修改了webpack配置,记得更新cache的version,否则可能会出现因为重用了缓存导致配置没生效的问题。

综上,最后代码修改如下:

  cache: {
    type: 'filesystem',
    version: '3.8.1',
  },
  optimization: {
    ...
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          mangle: false, // Note `mangle.properties` is `false` by default.
        },
      }),
    ],
    ...
  }

fork-ts-checker-webpack-plugin语法报错

Invalid configuration object. ForkTsCheckerWebpackPlugin has been initialized using a configuration object that does not match the API schema.
 - configuration has an unknown property 'reportFiles'. These properties are valid:
   object { async?, typescript?, formatter?, issue?, logger?, devServer? }

fork-ts-checker-webpack-plugin 是在单独的进程上运行 TypeScript 类型检查器的 Webpack 插件。当文件发生改动需要重新转译和类型检查时,fork-ts-checker-webpack-plugin会开辟一个单独的进程去执行类型检查的任务,这样就不会影响 webpack 重新编译的速度。

原来的配置:

plugins: [
  new ForkTsCheckerWebpackPlugin({
      memoryLimit: 4096,
      tsconfig: PATH.appDirectory + '/tsconfig.json',
      checkSyntacticErrors: true,
      reportFiles: [`${PATH.appSrc}/**/*.{ts,tsx}`],
    }),
]

fork-ts-checker-webpack-plugin从 4.1.3 升级到 7.2.11 ,这个plugin的API已经发生了改变。改成:

plugins: [
  new ForkTsCheckerWebpackPlugin({
      typescript: {
        memoryLimit: 4096,
        configFile: PATH.appDirectory + '/tsconfig.json',
        diagnosticOptions: { syntactic: false, semantic: false, declaration: false, global: false }
      },
    }),
]

diagnosticOptions选项用来设置哪些TypeScript语法需要检查。

IgnorePlugin报错

Invalid options object. Ignore Plugin has been initialized using an options object that does not match the API schema.
 - options should be one of these:
   object { resourceRegExp, contextRegExp? } | object { checkResource }
   Details:
    * options misses the property 'resourceRegExp'. Should be:
      RegExp
      -> A RegExp to test the request against.
    * options misses the property 'checkResource'. Should be:
      function
      -> A filter function for resource and context.

IgnorePlugin的作用是忽略第三包指定目录,让这些指定目录不要被打包进去。比如moment包的locale文件夹包括了各国语言的目录,如果把所有语言都打包进去就会影响打包效率。

原来的配置:

new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)

从报错来看,该plugin的语法发生了改变。改成:

new webpack.IgnorePlugin({
  resourceRegExp: /^\.\/locale$/,
  contextRegExp: /moment/,
})

devtool报错

Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
 - configuration.devtool should match pattern "^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$".
   BREAKING CHANGE since webpack 5: The devtool option is more strict.
   Please strictly follow the order of the keywords in the pattern.

大概的意思是说:检查devtool的匹配,webpack5要求的匹配更严格。

原来的配置:

devtool: isEnvDevelopment ? 'cheap-module-eval-source-map' : false,

改成:

devtool: isEnvDevelopment ? 'eval-cheap-module-source-map' : false,

webpack-dev-server publicPath报错

Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'publicPath'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, setupExitSignals?, static?, watchFiles?, webSocketServer? }

这次webpack-dev-server从 "^3.11.2" 升级到了 "^4.9.0"

publicPath用来设置项目跑在本地时,打包生成的文件所在的位置。

原来的配置:

devServer: {
    ...
    publicPath: appConfig.publicPath,
    ...
},

改成:

devServer: {
    ...
    devMiddleware: {
      publicPath: appConfig.publicPath,
    },
    ...
  },

webpack-dev-server contentBase报错

Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'contentBase'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, setupExitSignals?, static?, watchFiles?, webSocketServer? }

contentBase用来设置项目跑在本地时,不由webpack打包生成的文件的位置。

原来的配置:

devServer: {
    ...
    contentBase: PATH.appDirectory,
    ...
  },

devServer的v4中contentBase迁移到了static下,并且static的默认值是path.resolve(process.cwd(), 'public') 。改成:

devServer: {
    ...
    static: [
      {
        directory: PATH.appDirectory,
      },
    ],
    ...
  },

webpack-dev-server disableHostCheck报错

Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'disableHostCheck'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, setupExitSignals?, static?, watchFiles?, webSocketServer? }

原来的配置:

devServer: {
    ...
    disableHostCheck: true,
    ...
  },

改成:

devServer: {
    ...
    allowedHosts: "all",
    ...
  },

移除 node.js polyfill

Module not found: Error: Can't resolve 'crypto' in '/xxx/node_modules/crypto-js'

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 }

webpack5 以前,webpack 会包含 nodejs 核心模块的 polyfill,这样的话,比如安装了一个crypto模块,那么就可以直接使用,因为 node 的polyfill会自动启动。现在,webpack5 移除了 nodejs 的 polyfill,无法再直接使用类似crypto的模块了。

如果你想要使用类似crypto的 nodejs 核心模块,有两种方法:

1.在 webpack 配置文件的resolve中配置fallback

module.exports = {
    ...
    resolve: {
        fallback:  {
            "crypto": require.resolve("crypto-browserify"), // 如果不需要,那么就直接改为 false 就可以了
        }
    }
}

2.如果觉得上面的方法很麻烦,那么可以使用node-polyfill-webpack-plugin:

const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')

{
  ...
  plugins: [
    ...
    new NodePolyfillPlugin(),
    ...
  ]
}

导入json文件语法改变

export 'orderLimit'.'group_ids'.'includes' (imported as 'orderLimit') was not found in '../../../custom.config.json' (possible exports: 0, 1, 2)

原来:

// custom.config.json
{
  "orderLimit": {
    "group_ids": ["xxx"],
    "desc": ["xxx"]
  }
}

// 原来引用文件方式
import { orderLimit } from '../../../custom.config.json'

改成:

import orderLimit from '../../../custom.config.json'

打包报错unknown option ‘-p’

[webpack-cli] Error: Unknown option '-p'

原本的打包命令文件:

shellExec(
  'webpack -p --color --config ' + require.resolve('../config/webpack.config'),
)

改成如下:

shellExec(
  'webpack --mode production --color --config ' + require.resolve('../config/webpack.config'),
)

node版本过低

eslint@8.15.0: The engine "node" is incompatible with this module. Expected version "^12.22.0 || ^14.17.0 || >=16.0.0". Got "14.15.4"

项目跑CI的时候报错说CI的node镜像版本偏低了。现在eslint要求的node版本是16以上,但是公司node版本是14。于是找负责运维的同事升级了node版本。

图片编译问题

原来的配置:

          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'media/image/[name].[hash:8].[ext]',
            },
          },

之前处理图片用的是url-loader。如果图片比较小那么会被编译成base64的格式,然后和项目代码打包在一起;如果是比较大的图片则会放在build/media/image文件夹下。然而升级到webpack5以后出现了下面的情况:

图片除了按要求放在build/media/image文件夹下,竟然还多存了一份直接放在了build文件夹下。而且项目代码中图片的引用路径指向了这些放错位置的图片,但是这些图片的格式是错误的,根本显示不了图像。

在网上查了一下,上面的现象是因为webpack5已经自带了图片解析功能,再使用url-loader的话会出现图片重复打包的问题。现在有两种解决方案:

1.继续使用使用url-loader。

2.改用webpack5自身来解析图片

          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            type: 'asset',
            parser: {
              dataUrlCondition: {
                maxSize: 10000,
              },
            },
            generator: {
              filename: 'media/image/[name].[hash:8].[ext]',
            },
          },

看了一下npm,url-loader已经两年没有更新了。稳妥起见,还是webpack自带的图片解析吧。

打包文件命名问题

webpack4的时候,代码编译后的文件夹命名是按顺序自增的数字id:

这样就存在一个问题,如果命名排在前面的模块被移除了,那么后面的模块就会补位,这会导致即使后续模块内容没有发生变化,但是因为文件夹名称发生了改变,之前的缓存就会失效了。

现在webpack5让开发者可以自己设置chunk和module的命名方式:

optimization: {
  chunkIds: 'named', // 'natural' | 'named' | 'size' | 'deterministic'
  moduleIds: 'named',
}
  • natural 就是webpack5以前生产模式下的命名方式。
  • named 是根据项目文件路径生成名称,webpack以前和现在的开发环境模式默认使用这种命名方式
  • size 是根据模块大小来生成数字。
  • deterministic 是webpack5新增的命名方式,根据文件名称生成短hash,webpack5生产模式下默认启用。

废弃了ModuleConcatenationPlugin

项目打包完在线上运行的时候出现了报错:

Uncaught TypeError: Cannot read properties of undefined (reading 'call')

错的是这段代码:

function __webpack_require__(moduleId) {
		var cachedModule = __webpack_module_cache__[moduleId]
		if (void 0 !== cachedModule) return cachedModule.exports
		var module = (__webpack_module_cache__[moduleId] = {
			id: moduleId,
			loaded: !1,
			exports: {},
		})
		return (
			__webpack_modules__[moduleId].call(
				module.exports,
				module,
				module.exports,
				__webpack_require__
			),
			(module.loaded = !0),
			module.exports
		)
	}

webpack实现了一个加载函数 __webpack_require__(moduleId) 来加载模块。这个函数做了这些事情:

  • 根据模块Id在缓存中找是否有这个模块,如果有直接从缓存拿
  • 如果缓存没有,那么先新建这个模块的缓存,然后再返回模块

__webpack_modules__[moduleId].call() 会报错八成是因为webpack找不到对应模块,__webpack_modules__[moduleId] 的结果是 undefined

顺着控制台列出来的报错文件继续往下看,看看是哪里使调用了 __webpack_require__(moduleId)

    "./node_modules/@babel/runtime/helpers/esm/slicedToArray.js": function(__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
        "use strict";
       ...
        var arrayWithHoles = __webpack_require__(null);
        var unsupportedIterableToArray = __webpack_require__(null)
          , nonIterableRest = __webpack_require__(null);
        ...
    },

破案了,显然 __webpack_require__(null) 是有问题的。

在网上搜了一下,找到了解决方案:

之前公司webapck4的配置中,在生产环境会启用 new webpack.optimize.ModuleConcatenationPlugin()

plugins: [
  !isEnvDevelopment && new webpack.optimize.ModuleConcatenationPlugin()
]

看了一下webpack的开发文档,webpack在打包的时候会将各个模块打包成闭包,用了ModuleConcatenationPlugin之后会尽可能将模块放到一个闭包内,这样会提升代码在浏览器中的执行速度。这种操作还有一个专业名词,叫 Scope Hoisting,意思就是作用域提升。

不过现在ModuleConcatenationPlugin已经废弃了,把上面的代码删除以后,项目就能正常运行了。

升级前后对比

  第一次打包 第二次打包 第三次打包 打包体积
webpack4 325.75s 106.36s 105.89s 56.9MB
webpack5 252.91s 33.23s 23.86s 30.7MB

没有缓存,且不保留之前打包的文件

有缓存,但不保留之前打包的文件

有缓存,且保留之前打包的文件

总结

到此这篇关于webpack4升级到webpack5实战经验总结的文章就介绍到这了,更多相关webpack4升级到webpack5内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • webpack@v4升级踩坑(小结)

    之前看到各大公众号都在狂推 webpack 新版发布的相关内容,之前就尝试了升级,由于部分插件的原因,未能成功,现在想必过了这么久已经可以了,今天就来试一下在我的项目中升级会遇到哪些坑. 查阅更新日志 在安装更新之前,先大致浏览了一下更新日志,对大部分用户来说迁移上需要注意的应该就是这些点: 在命令行界面运行打包指令需要安装 webpack-cli : 打包需要指定打包模式 production or development ,在不同模式下会添加不同的默认配置, webpack.DefinePl

  • 记一次webpack3升级webpack4的踩坑经历

    webpack4版本也出了很久了 之前弄过一段时间的升级 后面因为种种原因搁浅了 今天有硬着头皮升级了一波 yeah 还好升级成功了 先贴一波原先webpack3的github配置 ps(我只是一个菜鸡= = webpack的配置很辣鸡 )废话少说 开撸 1 webpack升级到4.0版本并且安装webpack-cli yarn add webpack-cli global<br>yarn add webpack-cli -D 如果不对webpack-cli进行安装的话会报错 如下: The

  • webpack 1.x升级过程中的踩坑总结大全

    前言 大家应该都知道,Webpack 是一个前端资源加载/打包工具.它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源.因为最近在对博客做SSR,无奈vue ssr demo或例子都是基于webpack2的.博主在webpack1.x上折腾了许久,vue-ssr-server-bundle.json文件生成仍然还是遥遥无期.最后还是乖乖地开始了webpack升级之旅. 本文主要记录升级过程中遇到的一些坑和解决办法,可能有些遗漏了,能记多少记多少吧.话不多说了,来一

  • webpack3升级到webpack4遇到问题总结

    最近由于项目需要,需要对已有的两个vue项目进行webpack3升级到webpack4,此处记录一下整个升级过程的几个重要步骤,以及遇到的问题和解决方案. 1.更新webpack以及相关联插件,webpack4新增插件webpack-cli npm i -D webpack webpack-cli webpack-dev-server webpack-merge 2.运行npm run dev,报错:Error: webpack.optimize.CommonsChunkPlugin has b

  • webpack3.0升级4.0的方法步骤

    1.webpack 3.11升级4.26 为了提升打包效率,在webpack3.11基础之上做了升级,webpack4.0发布以来,零配置的webpack对项目本身提供的"打包"和"压缩"功能已经做了优化,如果在项目开始使用4.0而不用vue-cli的默认配置,遇到的问题或许能少一些. 2. 安装/升级依赖 这些依赖有的是在build过程中发现依赖有新的替换或者报错,逐步替换的,如果想遇到多个坑,可以先把webpack.webpack-cli升级到对应版本 devD

  • webpack4 升级迁移的实现

    好久不看 webpack已经从 3到4了,其实很早到今年2月份就开始发布 webpack 4 的 beta 版本: 每一次 webpack 的升级都还是建议阅读下 <webpack 4: released today! - Sean T. Larkin>.你需要明白,为什么 webpack又要迭代一个新的里程碑,以及带了什么新的特性: 这里简单总结下: Faster!!! Webpack 在 bundle bundle 的时间会缩短至少 60 个点,最高可以到 98%; (我们似乎可以节省一些

  • Vue项目从webpack3.x升级webpack4不完全指南

    前段时间,泡面将自己的一个Vue-cli构建的前端框架从webpack3.x升级到了4.x版本,现在才拉出来记录一下,已备忘之用,也和大家分享一下,以免大家采坑. 原先的环境 项目原先通过Vue-cli 2.9.3 版本构建,原先使用的webpack 3.x版本 首先需要对基础包进行更新(package.json) webpack 更新到4.x版本,泡面这里更新到了4.28.3 更新webpack-dev-server,泡面更新到了3.1.14版本, 安装webpack-cli,泡面安装的是3.

  • 详解webpack4升级指南以及从webpack3.x迁移

    几天前webpack发布了新版本v4.0.0,其中做了很多改动,包括0配置以及移除了CommonsChunkPlugin等.由此而来的还有之前webpack3.x的项目如何迁移到新的webpack版本,本文就一个新的vue-cli创建的基于webpack的项目进行迁移. 题外话:不要看0配置是很有噱头,基本是不能满足大部分用户啊的需求,不过加入了更多的默认配置确实也方便了用户,配置相对简单,是一种开箱即用的方式.毕竟之前parcel的0配置确实抢了很多webpack的风头,然后也去弄了一下par

  • webpack4升级到webpack5的实战经验总结

    目录 前言 terser-webpack-plugin语法报错 fork-ts-checker-webpack-plugin语法报错 IgnorePlugin报错 devtool报错 webpack-dev-server publicPath报错 webpack-dev-server disableHostCheck报错 移除 node.js polyfill 导入json文件语法改变 打包报错unknown option ‘-p’ node版本过低 图片编译问题 打包文件命名问题 废弃了Mod

  • 详解基于Vue cli生成的Vue项目的webpack4升级

    前面的话 本文将详细介绍从webpack3到webpack4的升级过程 概述 相比于webpack3,webpack4可以零配置运行,打包速度比之前提高了90%,可以直接到ES6的代码进行无用代码剔除,新增的optimization使用简单 在未来,CSS.HTMl和文件都会成为原生模块 [0配置] webpack4 设置了默认值,以便无配置启动项目 entry 默认值是 ./src/ output.path 默认值是 ./dist mode 默认值是 production [模块类型] web

  • 浅谈checkbox的一些操作(实战经验)

    checkbox看起来很简单,有时很头疼,有什么难的,google一下,代码都出来了,可是真的对吗?! 复制代码 代码如下: 1.通过$(selector).attr("checked")能获取到实际值? No,checkbox被选过一次后,结果一直是checked 2.通过$(selector).attr("checked", true)可以使checkbox被选中? No,你可以试试 那怎么办? 经过实验,总结如下: 复制代码 代码如下: 以下obj = doc

  • Javascript 多浏览器兼容总结(实战经验)

    一.document.formName.item("itemName") 问题 问题说明:IE下,可以使用 document.formName.item("itemName") 或 document.formName.elements["elementName"]:Firefox下,只能使用document.formName.elements["elementName"]. 解决方法:统一使用document.formName

  • 网站性能提高实战经验点滴记录

    DB: 1.数据库可以适当设计一些冗余字段来减少联合查询 2.经常查询的字段要建立索引 3.查询内容尽量简洁, 比如cakephp中的查询尽量设置$this->recursive=-1,并指定fields. 4.数据库用单独的服务器,有条件的常用查询数据单独分库 5.把session等数据放在Memcache而不是数据库中既能满足多服务器之间共享也能降低数据库的负载. 参考如下文章: http://www.linuxjournal.com/article/7451?page=0,1 6.Bad

  • Java开发反射机制的实战经验总结

    目录 前言 一.创建Class的三种方式 二.反射获取类的所有属性和属性类型 三.反射动态修改类属性的注解值 四.反射获取类的方法及调用方式 总结 前言 我在实际项目当中有经常用到反射机制,故而将学会的反射用法做一些汇总笔记,当做以后复盘所用. 存在这样一个类: package com.example.demo; import com.alibaba.fastjson.annotation.JSONField; public class User { private String name; @

  • MySQL闪回(flashback)原理与实战

    DBA或开发人员,有时会误删或者误更新数据,如果是线上环境并且影响较大,就需要能快速回滚.传统恢复方法是利用备份重搭实例,再应用去除错误sql后的binlog来恢复数据.此法费时费力,甚至需要停机维护,并不适合快速回滚.也有团队利用LVM快照来缩短恢复时间,但快照的缺点是会影响mysql的性能. MySQL闪回(flashback)利用binlog直接进行回滚,能快速恢复且不用停机.本文将介绍闪回原理,给出笔者的实战经验,并对现存的闪回工具作比较. 开胃菜 某天,小明因种种原因,误删了大批线上用

随机推荐