webpack4迁移webpack5
# webpack4迁移webpack5
# 准备
- 当前项目
webpack版本为v4.29.6,webpack-cli为v3.3.0 vue版本仍然还是v2,vue3的webpack配置,不在这篇博文里讨论~
# 升级
为减少升级成本,这里有个工具可以一键升级~
# 一键迁移
- 安装工具:
cnpm install -g npm-check-updates; - 项目根目录下执行命令:
npm-check-updates,检查更新:
boss-creator-web git:(feature/editor) ✗ npm-check-updates
Checking /Users/admin/work/boss-creator-web/package.json
[====================] 74/74 100%
@babel/core ^7.4.0 → ^7.15.8
@babel/preset-env ^7.4.2 → ^7.15.8
@vue/babel-preset-app ^3.6.0 → ^4.5.14
@vue/test-utils ^1.0.0-beta.29 → ^1.2.2
...
webpack ^4.29.6 → ^5.59.1
webpack-bundle-analyzer ^3.7.0 → ^4.5.0
webpack-cli ^3.3.0 → ^4.9.1
webpack-dashboard ^3.3.3 → ^3.3.6
webpack-dev-server ^3.2.1 → ^4.3.1
webpack-merge ^4.2.1 → ^5.8.0
...
- 检查完成后会输出
packge.json中插件当前版本和最新版本的对比,接下来输入ncu -u,会看到package.json中版本号已经更新为最新版了; - 最后删除
node_modules,执行npm install,安装~
# 升级
安装完成后,先试下 npm run build ,不出意外,应该是会报错的~
接下来挨个解决各个报错~
# 插件报错
HashedModuleIdsPlugin插件报错,报错信息如下:
TypeError: webpack.HashedModuleIdsPlugin is not a constructor
HashedModuleIdsPlugin 是 webpack v4 提供的插件,会根据模块的相对路径生成一个四位数的hash作为模块id,v4中用法是:
// webpack.config.base.js
new webpack.HashedModuleIdsPlugin({
// 选项……
});
v5 中用法为:
new webpack.ids.HashedModuleIdsPlugin({
// Options...
});
解决方法: 将配置文件中 v4 用法改为 v5 用法即可~
参考:hashed-module-ids-plugin v4用法 (opens new window)、hashed-module-ids-plugin v5用法 (opens new window)
CleanWebpackPlugin插件报错,报错信息为:
TypeError: CleanWebpackPlugin is not a constructor
这个也是插件升级引入方式的的改变造成的报错,该插件由 v2.0.1 升级为了 v4.0.0 ,修改引入方式即可:
// webpack.config.prod.js
// const CleanWebpackPlugin = require('clean-webpack-plugin'); // 旧用法
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); // 新用法
webpack-merge插件报错:
[webpack-cli] Failed to load '/Users/admin/work/boss-youle-web/build/webpack.config.prod.js' config
[webpack-cli] TypeError: merge is not a function
webpack-merge 由 v4.2.1 升级到 v.5.8.0,引入方式也改变了,修改即可:
// const merge = require('webpack-merge'); // 旧用法
const {merge} = require('webpack-merge'); // 新用法
- optimize-css-assets-webpack-plugin 插件警告:
(node:3251) [DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS] DeprecationWarning: optimizeChunkAssets is deprecated (use Compilation.hooks.processAssets instead and use one of Compilation.PROCESS_ASSETS_STAGE_* as stage option)
optimize-css-assets-webpack-plugin 在 webpack4 主要是用来压缩css的插件,webpack5 中则 可以通过 css-minimizer-webpack-plugin 来替换:
- 安装:
npm install css-minimizer-webpack-plugin --save-dev - 配置:
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 引入
optimization: {
minimize: true, // 开发环境下启用 CSS 优化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
`...`,
new CssMinimizerPlugin(),
],
}
- 最后删掉
optimize-css-assets-webpack-plugin的使用即可~
- mini-css-extract-plugin 警告:
WARNING in chunk commons [mini-css-extract-plugin]
Conflicting order. Following module has been added:
可以通过将插件的 ignoreOrder 选项设置为 true 来禁用 css order 警告:
// webpack.config.prod.js
plugins: [
new MiniCssExtractPlugin({
ignoreOrder: true,
}),
],
# 配置优化
- optimization.splitChunks 报错:
Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.optimization.splitChunks should be one of these:
false | object { automaticNameDelimiter?, cacheGroups?, chunks?, defaultSizeTypes?, enforceSizeThreshold?, fallbackCacheGroup?, filename?, hidePathInfo?, maxAsyncRequests?, maxAsyncSize?, maxInitialRequests?, maxInitialSize?, maxSize?, minChunks?, minRemainingSize?, minSize?, name?, usedExports? }
-> Optimize duplication and caching by splitting chunks by shared modules and cache group.
configuration.optimization.splitChunks.name should be one of these:
false | string | function
optimization.splitChunks.name 设置拆分chunk的名称,webpack4 中设置为 true 将自动生成基于块和缓存组键的名称;
但 v5 中不能传 true,只能传 false | string | function;设为 false 将保持 chunk 的相同名称,因此不会不必要地更改名称。这是生产环境下构建的建议值。
// webpack.config.prod.js
optimization: {
splitChunks: {
// name: true // v4写法
name: false // 新写法,设置为false,或者注释掉也可以
}
}
- DefinePlugin 警告:
WARNING in DefinePlugin
Conflicting values for 'process.env.NODE_ENV'
webpack5 中 process.env.NODE_ENV 默认值取决于 mode,也可在 optimization.nodeEnv 中设置;取消 DefinePlugin 中的配置即可解除警告:
// webpack.config.base.js
plugins: [
new webpack.DefinePlugin({
'process.env': {
// NODE_ENV: JSON.stringify(`${process.env.NODE_ENV}`), // webpack5中 process.env.NODE_ENV 默认值取决于 mode
PUBLIC_PATH: JSON.stringify(`${argv.publicPath}`),
PATH_PREFIX: JSON.stringify(`${argv.pathPrefix}`),
RUN_ENV: JSON.stringify(`${process.env.RUN_ENV}`)
}
}),
]
2021-11-20更新:不用注释,在脚本中添加
NODE_ENV全局变量即可解除警告:
"dev": "cross-env NODE_ENV=development RUN_ENV=dev webpack serve --progress --config build/webpack.config.dev.js",
- hash 警告:
(node:99549) [DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_HASH] DeprecationWarning: [hash] is now [fullhash] (also consider using [chunkhash] or [contenthash], see documentation for details)
这个警告提示我们要把项目中之前在 webpack4 中使用 hash 配置的改成 chunkhash 或者 contenthash , webpack 的官方迁移指南里有说明:
// webpack.config.prod.js
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: utils.getPublicPath(),
filename: 'js/[name]_[chunkhash:8].js', // hash更改为 chunkhash or contenthash
chunkFilename: 'js/[name]_[chunkhash:8]_chunk.js',
libraryTarget: 'umd',
umdNamedDefine: true
},
- 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$".
这是因为 devtool 在 webpack4 和 webpack5 上也是有区别的,修改如下:
// webpack.config.dev.js
// devtool: 'cheap-module-eval-source-map', // webpack4写法
devtool: 'eval-source-map', // webpack5写法
- hard-source-webpack-plugin 报错:
TypeError: Cannot read property 'tap' of undefined
at Object.exports.tap (/Users/admin/work/boss-youle-web/node_modules/hard-source-webpack-plugin/lib/util/plugin-compat.js:118:25)
at new CacheSerializerFactory (/Users/admin/work/boss-youle-web/node_modules/hard-source-webpack-plugin/lib/CacheSerializerFactory.js:94:18)
at HardSourceWebpackPlugin.apply (/Users/admin/work/boss-youle-web/node_modules/hard-source-webpack-plugin/index.js:219:36)
在 webpack4 的开发环境中,我们使用 hard-source-webpack-plugin 来实现启动缓存,从而实现启动提速;但在 webpack5 中内置了 cache 缓存机制,直接配置即可,hard-source-webpack-plugin 就直接被废弃掉了。
// webpack.config.dev.js
cache: {
// 将缓存类型设置为文件系统
type: "filesystem",
buildDependencies: {
/* 将你的 config 添加为 buildDependency,
以便在改变 config 时获得缓存无效*/
config: [__filename],
/* 如果有其他的东西被构建依赖,
你可以在这里添加它们*/
/* 注意,webpack.config,
加载器和所有从你的配置中引用的模块都会被自动添加*/
},
// 指定缓存的版本
version: '1.0'
}
cache 具体配置可参考 (opens new window)
- devServer 报错:
Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options has an unknown property 'quiet'. 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 在 webpack5 中的配置相较 v4 有些变化,修改如下:
// webpack.config.dev.js
devServer: {
hot: true,
// contentBase: path.join(__dirname, 'dist'), // webpack4 写法
compress: true,
host: '127.0.0.1',
port: 8098,
open: true,
// webpack5新增 static 字段,里面的 directory 替换原来 contentBase 的配置;publicPath 也放在了里面
static: {
directory: path.join(__dirname, 'dist'),
publicPath: '/',
},
// publicPath: '/', // webpack4写法
// quiet: true, // webpack5取消了这个参数
...
},
具体配置参数可参考: v4配置 (opens new window)、v5配置 (opens new window)
还有一个地方需要注意的是, webpack-cli4之后执行脚本上有了变化:
"scripts": {
"dev": "cross-env RUN_ENV=dev webpack-dev-server --progress --config build/webpack.config.dev.js", // 旧写法
"dev": "cross-env RUN_ENV=dev webpack serve --progress --config build/webpack.config.dev.js", // 新写法
}
# 资源模块
在
webpack5之前,加载图片、字体等资源需要我们使用url-loader、 file-loader等来处理; 从webpack5开始,我们可以使用内置的资源模块来替代这些loader的工作。
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。参考 (opens new window)
以图片静态资源解析为例:
// webpack.config.base.js
{
test: /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/, // 解析图片等静态资源
dependency: { not: ['url'] }, // 排除来自新 URL 处理的 asset
exclude: [ path.resolve('src/assets/svg')], // 排除
// use: {
// loader: 'url-loader',
// options: {
// limit: 1024, // 字节数,意味着当图片大小小于1k的话,打包时会进行base64转换
// name: utils.assetsPath('images/[name].[hash:7].[ext]')
// }
// },
type: "asset/resource",
generator: {
// 这里的 [ext] 扩展名通配符包含了 . ,我们不需要额外再写,跟之前的 loader 有所区别
filename: utils.assetsPath('images/[name]_[hash:7][ext][query]')
},
},
# 其他问题
- eslint-loader 报错:
ERROR in ./src/main.js
Module build failed (from ./node_modules/eslint-loader/index.js):
Error: Cannot find module '@eslint/eslintrc/universal'
升级后的版本不稳定,降至之前的稳定版本即可:
// eslint相关插件退至升级前版本
"eslint": "^5.16.0",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.1.2",
"eslint-plugin-html": "^5.0.3",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.2.2",
- babel-eslint 报错
ERROR in ./src/plugins/global/video-player/index.vue
Module build failed (from ./node_modules/_eslint-loader@2.2.1@eslint-loader/index.js):
TypeError: token.type.endsWith is not a function
原因同上,把 babel-eslint 退至 v8.2.6 稳定版即可~
以上就是我在升级过程中遇到的问题,都解决后,执行 npm run dev,成功启动;npm run build,成功打包~
# 区别
devServer的参数配置有变化- 图片等静态资源的解析有变化,Webpack5 提供了内置的静态资源构建能力,我们不需要安装额外的
loader(url-loader,file-loader,raw-loader) - webpack5 中内置了
Cache来实现启动缓存,实现了二次构建的提速, v4 需要引入插件hard-source-webpack-plugin - js压缩:
webpack v5开箱即带有最新版本的terser-webpack-plugin。如果希望自定义配置,那么仍需要安装terser-webpack-plugin。v4 则必须安装terser-webpack-plugin v4的版本 - 启动服务差别:v4 通过
webpack-dev-server启动服务,v5 内置使用webpack serve启动 tree-shaking的优化:v5能够处理对嵌套模块的tree shaking,也能处理对Commonjs的tree shaking
https://github.com/webpack/changelog-v5 (opens new window)