10分钟带你升级webpack5
前言
最近很多小伙伴们都在问vite
都出来了,webpack
还有必要升级到5
么?vite
是不是很快就要取代webpack
了?
针对这个问题,我个人观点是:适合你项目的构建工具就是最好的
。当前前端的构建工具有很多种,从早期的gulp
、再到ES
时代的rollup
、webpack
等;每种构建工具都有自己的不同特性,主要是看公司以及你的团队所需,不一定有了新工具出来我们就一定要立马去学习,立马抛弃生产环境正在跑的工具,经历过大浪的洗礼,最终活下来的才是真正值得我们学习的
。
在上一篇文章你不得不知道的webpack性能优化中,我们主要针对webpack4
如何去做打包构建的优化,距离webpack5
的发布已经有大半年了,相信有很多小伙伴们已经在自己的项目中升级了webpack5
,也体验到了webpack5
打包构建时带来的飞一般的感觉。
webpack4跟webpack5构建效率对比
webpack4构建的项目地址:github.com/Paulinho89/…
webpack5构建的项目地址:github.com/Paulinho89/…
我们来两张打包的速度截图比对一下:
webpack4
webpack5
两个不同的项目,但是同样的打包文件,图1是使用webpack4
构建、图2是使用webpack5
构建。在第三次构建之后的时间对比可以发现,使用webpack4
需要2278ms
,而使用webpack5
仅仅需要550ms
,相差了近4倍
;没错,这就是webpack5
的最大魅力所在,尤其是在大型复杂项目中,webpack5
在构建效率的提升上会更加明显;
踩坑指南
笔者也在近期将项目升级到了webpack5
,因为项目的脚手架
是手动搭建
,没有基于官方的cli
,所以在升级的过程中难免踩了不少坑,这篇文章主要跟大家分享一下踩坑历程。
package.json中的插件升级
既然是从webpack4
升级到webpack5
,相信很多小伙伴第一时间可能先会想到直接把webpack
、webpack-cli
升级到最新的版本不就好了。
"webpack": "^5.38.1",
"webpack-cli": "^4.7.0"
如果你只是单纯的这样手动的去升级包的话,很快你就会遇到各种各样的loader
跟plugin
语法报错;
所以为了减少升级的成本,所以不建议通过手动去升级包的版本,那有没有一键升级
包的工具呢?答案肯定是有的。
npm-check-updates
- 安装
cnpm install -g npm-check-updates
- 在
package.json
的跟目录下执行npm-check-updates
npm-check-updates
- 执行
ncu -u
检查package.json
文件
ncu -u
你会发现
package.json
里面的依赖版本号已经变成最新版本
- 执行
npm install
下载最新的依赖
npm install
loader跟plugin配置升级
经过上述package.json
中的插件一键升级以后,我们再次尝试执行npm run build:prod
命令打包试试,发现有报错。
跟webpack4
对比后发现,webpack5
中最新的merge
需要使用解构的方式来配置。
webpack4
const Merge = require('webpack-merge');
module.exports = WebpackMerge(WebpackConfig, {
mode: "production",
devtool: "hidden-source-map",
entry: {
app: resolve(__dirname, "../src/main")
},
plugins: [
new Webpack.DllReferencePlugin({
manifest: require("../library/library.json")
})
]
});
webpack5
const { merge: WebpackMerge } = require("webpack-merge");
module.exports = WebpackMerge(WebpackConfig, {
mode: "production",
devtool: "hidden-source-map",
entry: {
app: resolve(__dirname, "../src/main")
},
plugins: [
new Webpack.DllReferencePlugin({
manifest: require("../library/library.json")
})
]
});
我们再次尝试执行npm run build:prod
命令打包试试,又发现有新的报错
经排查发现原来在webpack4
中url-loader
使用loaders
的配置已经被废弃掉,而在webpack5
中url-loader
在使用options
配置的时候必须要使用use
数组,相应的loader
也要使用对象的形式来书写。
webpack4
{
test: /\.(jpg|jpeg|png|gif)$/,
loaders: 'url-loader',
exclude: /node_modules/,
options: {
limit: 8192,
outputPath: 'img/',
name: '[name]-[hash:6].[ext]'
}
}
webpack5
{
test: /\.(jpg|jpeg|png|gif)$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
outputPath: "img/",
name: "[name]-[hash:6].[ext]"
}
}
]
}
我们再次尝试执行npm run build:prod
命令打包试试,又发现有新的报错。
经排查发现原来在webpack4
中经常使用的hard-source-webpack-plugin
来缓存我们的打包文件,在webpack5
中被废弃掉了;喜欢思考的小伙伴们也许就会发问,上边都描述了webpack5
比webpack4
构建效率上有大大的提升,那webpack5
它构建的效率提升到底体现在哪地方呢?个人觉得就是webpack5
中最大的优势就是它的持久化缓存能力
,那webpack5
中我们是如何开启缓存的呢?
webpack4
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
plugins: [
new HardSourceWebpackPlugin()
]
}
webpack5
module.exports = {
cache: {
// 将缓存类型设置为文件系统
type: "filesystem",
buildDependencies: {
/* 将你的 config 添加为 buildDependency,
以便在改变 config 时获得缓存无效*/
config: [__filename],
/* 如果有其他的东西被构建依赖,
你可以在这里添加它们*/
/* 注意,webpack.config,
加载器和所有从你的配置中引用的模块都会被自动添加*/
},
// 指定缓存的版本
version: '1.0'
}
}
我们再次尝试执行npm run build:prod
命令打包后,找到node_modules/.cache
目录会发现
每次我们执行完打包命令后,为了防止缓存过于固定,导致更改构建配置无感知,webpack5
依然使用旧的缓存,默认情况下,每次修改构建配置文件都会导致重新开始缓存。当然也可以自己主动设置 version
来控制缓存的更新。
原理
webpack5
增加了确定的 moduleId
,chunkId
的支持
optimization.moduleIds = 'deterministic'
optimization.chunkIds = 'deterministic'
-
这个配置在
mode:production
的时候是默认开启的,它的作用是以确定的方式为module
和chunk
分配3-5
位数字id
,相比于webpack4
版本的选项hashed
,它会导致更小的文件bundles
。 -
由于
moduleId
和chunkId
确定了,构建的文件的hash
值也会确定,有利于浏览器长效缓存
。同时此配置有利于减少文件打包大小。这个也是webpack5持久化缓存的秘诀之一。
打包没有报错后,接下来我们开始执行npm run dev
看看开发环境
启动有没有问题。
因为我们的脚手架在开发环境使用的是webpack-dev-middleware
配合express
来启动本地前端服务的,经排查在webpack5
中废弃掉了compiler.plugin()
的方式调用。
webpack4
const compiler = Webpack(WebpackConfig);
// webpack5废弃改写法
compiler.plugin("compilation", compilation => {
compilation.plugin("html-webpack-plugin-after-emit", () => {
hotMiddleware.publish({ action: "reload" });
});
});
compiler.apply(new DashboardPlugin());
所以,我们按照官方文档的说法采用webpack serve
来启动我们的本地服务
webpack4
"scripts": {
"start": "node build/dev-server.js"
}
webpack5
"scripts": {
"start": "webpack serve --config build/webpack.config.dev.js"
}
再次执行npm run dev
后,发现每次启动都会有一个关于hash
配置的警告
这个警告提示我们要把项目中之前在webpack4
中使用hash
配置的改成chunkhash
或者contenthash
webpack4
module.exports = {
output: {
filename: "js/[name]-[hsah:6].js",
chunkFilename: "js/[name]-[chunkhash:6].js",
path: resolve(__dirname, "../dist")
}
}
webpack5
module.exports = {
output: {
filename: "js/[name]-[chunkhash:6].js",
chunkFilename: "js/[name]-[chunkhash:6].js",
path: resolve(__dirname, "../dist")
}
}
再次执行npm run dev
后,启动终于算是正常了。但是当我们访问系统中带有图片的页面的时候,发现使用require
引入图片都无法正常加载展示。审查元素发现,图片路径无法正常的编译。
经过排查发现,我们需要在url-loader
配置中添加esModule: false
才可以把require
引入的图片正常的编译。
webpack5
module.exports = {
{
test: /\.(jpg|jpeg|png|gif)$/,
use: [
{
loader: "url-loader",
options: {
// 新增一行这个配置
esModule: false,
limit: 8192,
outputPath: "img/",
name: "[name]-[chunkhash:6].[ext]"
}
}
]
}
}
总结
当然,上述仅仅是我摘取几个比较有代表性的打包报错,没有把所有的报错一一给大家罗列出来。具体的细节还需要小伙伴们自己动手去折腾,这样才能完整的体验到整个升级带来的快(痛)乐(苦)
;尤其是你在历经各种各样奇奇怪怪的报错,然后又一个个的将其修复的时候,内心还是会有一些成就感的,最重要的是webpack5
在构建的效率上确实相比webpack4
有了非常大幅的提升。
所以,webpack5
中除了在构建上利用其强大的持久化缓存,将构建的效率有非常大的提升之外,还有一些非常令人兴奋的新特性
- 更优的 tree-shaking
- Module Federation
- Node Polyfill 脚本被移除
- ...
尤其是Module Federation,有点类似于云组件的概念,跟微前端的理念非常类似。它使 JavaScript 应用得以从另一个 JavaScript 应用中动态地加载代码 —— 同时共享依赖。相当于 webpack 提供了线上 runtime 的环境,多个应用利用 CDN 共享组件或应用,不需要本地安装 npm 包再构建了。
后续有机会我也会在单页的项目中尝试使用Module Federation
。
好了,关于单页脚手架的搭建
以及webpack
在我们项目中的优化,已经连续输出了3篇相关的文章了,后续会针对其他类型的文章做专题输出,比如浏览器
、网络
、设计模式
、前端性能优化
等,敬请期待!
如果您觉得这篇文章对您有一点点帮助,欢迎您看完后给我点赞,您的点赞是我前进的动力,谢谢~~
转载自:https://juejin.cn/post/6973607639502553095