一、webpack5 新特性
在前端打包优化的方案中有一种最为简单直接, 那便是使用更高版本的打包工具, webpack 于 2020.10.10 更新了 webpack5 版本, 至今已有近一年的时间, 恰巧在对一个基于 webpack3 的项目进行性能优化, 便记录一下 webpack3 升级 webpack5 的过程.
关于 webpack5 有哪些具体的变更, 可参考官方文档 webpack5 发布日志
以下为部分更新内容:
- 功能清除
- require.includes 废弃
- 不再为 Node.js 模块引入 polyfill
- 长期缓存
- 确定的 chunk、模块ID 和导出名称
- 开发支持
- 模块联邦
- ModuleFederationPlugin 插件
- 模块联邦
- 构建优化
- 嵌套的 tree-shaking
- 内部模块 tree-shaking
- 性能优化
- 持久化缓存
- 代码生成
- 支持 es6 代码
二、webpack3 升级 webpack5 的改动
需要注意的是升级到 webpack5 , 你的 node 版本需要
>= 10.13.0
npm 6.13.4
node v12.15.0
升级依赖, 以下为本次升级过程中改动的依赖及其版本
npm install webpack@5.74.0 -D
npm install webpack-cli@4.10.0 -D
npm install webpack-dev-server@4.10.1 -D
npm install webpack-merge@5.8.0 -D
npm install copy-webpack-plugin@9.1.0 -D
npm install vue-loader@15.10.0 -D
npm install html-webpack-plugin@5.5.0 -D
npm install mini-css-extract-plugin@2.6.1 -D
npm install css-minimizer-webpack-plugin@4.0.0 -D
webpack5 的启动命令稍作修改, 需要变更为 webpac serve
// package.json
// "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"dev": "npx webpack serve --config build/webpack.dev.conf.js --color --progress",
替换 devtool
// devtool: 'cheap-module-eval-source-map',
devtool: 'eval-cheap-module-source-map',
merge 需要从依赖包中解构, 而不是直接导出, 所有引用到的地方都需要修改
// const merge = require('webpack-merge')
const { merge } = require('webpack-merge')
需要将中node属性中的设置改到 resolve 的 fallback 属性中。
// webpack.base.conf.js
module.exports = {
// node: {...},
resolve: {
fallback: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
}
}
添加 VueLoaderPlugin
// webpack.base.config.js
const { VueLoaderPlugin } = require('vue-loader') // vue版本2.6.14
module.exports = {
// ...
plugins: [
new VueLoaderPlugin(),
// ...
]
}
webpack5改用配置方式, 插件作用: 在热加载时直接返回更新文件名
// webpack.dev.config.js
// new webpack.NamedModulesPlugin(),
module.exports = {
optimization: {
moduleIds: 'named' // webpack5 采用此方式代替 NamedModulesPlugin
}
}
// webpack.prod.config.js
// new webpack.HashedModuleIdsPlugin(),
module.exports = {
optimization: {
moduleIds: 'hashed', // webpack5 采用此方式代替 HashedModuleIdsPlugin
}
}
CopyWebpackPlugin 的参数格式有所变化, 需要在对应的文件中改动
// webpack.dev.config.js、webpack.prod.config.js
// new CopyWebpackPlugin([
// {
// from: path.resolve(__dirname, '../static'),
// to: config.dev.assetsSubDirectory,
// ignore: ['.*']
// }
// ]),
new CopyWebpackPlugin({
patterns:[{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory, // 在webpack.prod.config.js 中要改成 config.build.assetsSubDirectory
globOptions: { // webpack5 ignore要写在globOptions这里
ignore: ['.*']
}
}]
}),
webpack5 弃用了之前的 css 处理插件, 改用下列插件代替
// utils.js
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// utils.js generateLoaders function
return [{loader: MiniCssExtractPlugin.loader}].concat(loaders)
// return ExtractTextPlugin.extract({
// use: loaders,
// fallback: 'vue-style-loader'
// })
return [{loader: 'vue-style-loader'}].concat(loaders)
// return ['vue-style-loader'].concat(loaders)
// extract-text-webpack-plugin已被移除,由mini-css-extract-plugin插件替代
// optimize-css-assets-webpack-plugin已过时, 由css-minimizer-webpack-plugin插件代替
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
plugins: [
// 删除ExtractTextPlugin、OptimizeCSSPlugin插件引用
new MiniCssExtractPlugin(),
],
optimization: {
minimizer: [
new CssMinimizerWebpackPlugin(),
]
}
}
devServer 中有部分字段变更, 可参照下述代码修改
// webpack.dev.conf.js
devServer: {
// clientLogLevel: 'warning', // v4 移动到了client下面 改名logging 'warn'
// contentBase: false, // v4移动到了static下面
// overlay: config.dev.errorOverlay
// ? { warnings: false, errors: true } // v4 移动到了client下面
// : false,
// publicPath: config.dev.assetsPublicPath, // v4 移动到了devMiddleware下面
// quiet: true, // v4移除了该选项
// watchOptions: {
// poll: config.dev.poll, // v4移动到了static下面的watch
// },
// disableHostCheck: true // v4移除了该选项
client: {
logging: 'warn',
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
progress: true,
},
static: false,
devMiddleware: {
publicPath: config.dev.assetsPublicPath
},
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
proxy: config.dev.proxyTable,
allowedHosts: "all"
}
此外之前通过 new webpack.optimize.CommonsChunkPlugin 拆分代码, 现在通过optimization.splitChunks 实现
HtmlWebpackPlugin 中的 chunksSortMode 现在只有’none’ | ‘auto’ | 'manual’三种配置,目前配置成 auto 即可
三、可能遇到的问题
1、[webpack-dev-middleware] HookWebpackError: Not supported
这个其实是 copy-webpack-plugin 的报错, 原因是该插件的版本与 node 的版本不兼容, 如果你的 node 版本 在 12.20.0 以下, 则应该升级 node 版本或者降低插件的版本到10以下
// 升级 node 版本
npm install node@latest -g
// 降低 copy-webpack-plugin 版本
npm install copy-webpack-plugin@9 -D
2、“exports is not defined”
将 .babelrc 文件中的 "modules": false, 注释掉即可