前言
之前学的是 webpack4,现在来学习一下 webpack5,学一下 webpack5 与 webpack4 的区别,整个过程让我对这次大版本更新有了深刻的理解。今天就来和大家分享 Webpack 5 的核心升级内容,以及如何在项目中实际应用这些新特性。
一、 性能飞跃:持久化缓存
痛点回顾
还记得在 Webpack 4 时代,每次启动项目都要经历漫长的等待吗?特别是大型项目,动辄 20-30 秒的启动时间,严重影响开发效率。
Webpack 5 的解决方案
开箱即用的持久化缓存让我眼前一亮:
javascript
// webpack.config.js
module.exports = {
cache: {
type: 'filesystem', // 使用文件系统缓存
cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'),
buildDependencies: {
config: [__filename], // 当配置文件变化时,缓存失效
},
},
}
实测效果
在我们项目中(使用 lodash 530KB + moment 170KB):
场景 | Webpack 4 | Webpack 5 |
---|---|---|
首次构建 | 1228ms | 1228ms |
二次构建 | 1150ms | ⚡️ 449ms |
开发体验 | 每次保存都要等 | 几乎秒级响应 |
使用建议:
- 开发环境:推荐使用
memory
缓存,速度更快 - 生产环境:使用
filesystem
缓存,持久化保存
二、 资源处理:告别繁琐的 loader 配置
曾经的配置噩梦
在 Webpack 4 中,处理资源文件需要配置一堆 loader:
file-loader
:生成单独文件url-loader
:转换为 Data URLraw-loader
:导入原始内容
javascript
// Webpack 4 配置
module.exports = {
module: {
rules: [
{
test: /.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
fallback: 'file-loader'
}
}
]
}
]
}
}
Webpack 5 的优雅解决方案
原生支持资源模块,无需额外 loader。
javascript
module.exports = {
output: {
assetModuleFilename: 'assets/[hash][ext][query]' // 资源文件输出路径
},
module: {
rules: [
// PNG文件 - 生成单独文件
{
test: /.png$/,
type: 'asset/resource' // 类似 file-loader
},
// JPG文件 - 转换为 Data URL
{
test: /.jpg$/,
type: 'asset/inline' // 类似 url-loader
},
// TXT文件 - 导入原始内容
{
test: /.txt$/,
type: 'asset/source' // 类似 raw-loader
},
// GIF文件 - 自动选择处理方式
{
test: /.gif$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8kb以下转为Data URL
}
},
generator: {
filename: 'images/[hash][ext]' // 覆盖全局配置
}
}
]
}
}
资源模块类型速查表
类型 | 对应 loader | 功能描述 |
---|---|---|
asset/resource |
file-loader |
生成单独文件,输出到指定目录 |
asset/inline |
url-loader |
转为 Data URL(base64) |
asset/source |
raw-loader |
导入文件的原始内容 |
asset |
自动选择 | 根据文件大小自动选择 resource 或 inline |
- PNG文件 :生成单独文件,路径如
/assets/841ed.png
- JPG文件 :转换为 Data URL 格式,如
data:image/jpeg;base64,...
- GIF文件:根据大小决定是生成文件还是 Data URL
- TXT文件:直接输出文件原始内容
三、 语法革新:Top-level Await
开发体验的质的飞跃
以前的写法(回调地狱):
javascript
// 必须位于 async 函数内部
async function init() {
const resp = await axios('...');
}
init();
现在的写法(直截了当):
javascript
// 直接在模块顶层使用
const resp = await axios('...');
console.log(resp.data);
开启配置
javascript
module.exports = {
experiments: {
topLevelAwait: true // 开启实验性功能
}
}
实现原理
Webpack 将顶级 await 代码包裹在自动生成的 async 函数中执行,让开发者可以更自然地编写异步代码。
适用场景:
- 模块初始化
- 动态导入
- 配置加载
- 权限验证
四、 Tree Shaking 优化:打包体积再瘦身
真实案例对比
案例一:简单模块优化
javascript
// index1.js
console.log("这个模块没有依赖,也没有导出");
打包结果对比:
- Webpack 4: 114字节(包含模块包装代码)
- Webpack 5: ⚡️ 80字节(直接输出)
案例二:复杂依赖分析
javascript
// index2.js → moduleA → moduleB
// 实际只使用了 f1 和 f4 函数
优化效果:
- 原始代码:312字节
- Webpack 5 打包后:⚡️ 94字节
- 优化关键:深度分析嵌套依赖,精准剔除未使用代码
配置建议
javascript
module.exports = {
optimization: {
usedExports: true,
sideEffects: false,
concatenateModules: true,
}
}
五、 构建清理:内置功能更简洁
告别 clean-webpack-plugin
Webpack 4 的方式:
javascript
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}
Webpack 5 的方式:
javascript
module.exports = {
output: {
clean: true // 一行配置搞定
}
}
六、 模块联邦:微前端的未来
革命性的代码共享方案
javascript
// app1/webpack.config.js (提供方)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button',
},
shared: ['react', 'react-dom'],
}),
],
};
// app2/webpack.config.js (消费方)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app2',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
},
}),
],
};