Webpack 5 正式发布已经有一段时间,但它带来的变化至今仍在影响前端工程的构建方式。相比 Webpack 4,这一代版本并不是简单的小修小补,而是从缓存机制、模块共享到产物优化都做了相当深度的重构。这篇文章重点聊聊其中最值得关注的几个新特性,包括运行时模块共享的 Module Federation、基于文件系统的持久化缓存,以及更好的 Tree Shaking 支持,并结合实际配置说明如何在项目中用起来。

Module Federation:运行时共享模块的微前端方案
Module Federation 中文常译作模块联邦,它解决的核心问题是:多个独立开发、独立部署的前端应用,如何在运行时互相加载对方的模块,同时避免公共依赖被重复打包。在它出现之前,实现微前端通常要借助 single-spa 这类框架,或者用 iframe 硬隔离,前者接入成本高,后者通信和体验都有硬伤。模块联邦直接在打包层面提供了原生的共享能力,可以说是 Webpack 5 最具想象力的特性。
它的配置主要由两部分组成。提供方通过 exposes 把内部模块暴露出去,消费方通过 remotes 声明远程入口。双方再通过 shared 约定公共依赖的共享策略,比如 React、Vue 这类库,只要版本满足要求就复用同一份实例,避免出现多个 React 实例导致的 hooks 报错。
// 提供方应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
filename: 'remoteEntry.js',
remotes: {
// 引用另一个独立部署的应用
shopApp: 'shopApp@https://cdn.ipipp.com/shop/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};
// 消费方异步加载远程模块
const ProductList = React.lazy(() => import('shopApp/ProductList'));
function App() {
return (
<Suspense fallback={"加载中"}>
<ProductList />
</Suspense>
);
}需要注意的是,singleton: true 表示整个运行时只允许存在一个该依赖的实例,这在 React 项目里几乎是必选项。如果两个应用的版本差距过大,低版本一方还可以配置 strictVersion 让构建直接报错,把版本冲突扼杀在 CI 阶段而不是线上白屏。
持久化缓存:二次构建速度的数量级提升
Webpack 5 移除了 Webpack 4 中实验性质的 cache-loader 和 dll 方案,转而内置了基于文件系统的持久化缓存。开启方式非常简单,只需在配置中加上 cache: { type: 'filesystem' },构建器会把模块解析结果、依赖图、代码生成产物等中间状态序列化到 node_modules/.cache/webpack 目录。下次启动时直接读取这些快照,跳过大部分编译工作。
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
// 配置文件变化时让缓存失效
config: [__filename],
},
version: 'prod-v1',
},
};在包含数千个模块的中大型项目里,冷启动构建可能需要一两分钟,而命中缓存的二次构建往往能压缩到十几秒,提速幅度非常可观。缓存的安全性依赖文件的时间戳和内容哈希,Webpack 会自动检测哪些模块发生了变化,只重新编译受影响的部分。此外 buildDependencies 的配置很关键,如果不把 webpack 配置文件、babel 配置纳入依赖,改了 loader 选项但缓存没有失效,就会出现奇怪的构建结果,这类问题排查起来相当费时间。
还有一个配套的细节值得了解:Webpack 5 在持续监听模式下对内存的占用明显下降,官方对编译器内部做了不少优化,即使不开持久化缓存,单次全量构建的速度相比 Webpack 4 也有提升,只是幅度远不如缓存命中时那么夸张。
产物优化:更彻底的 Tree Shaking 与移除默认 Polyfill
Tree Shaking 在 Webpack 5 中得到了两个重要增强。一是支持嵌套的无用导出消除,即使某个导出被层层转手,只要最终没人使用,整条链路都会被裁掉;二是新增了对 CommonJS 模块的部分分析能力,配合 sideEffects 字段,能更准确地判断哪些文件可以安全跳过。这对于依赖 lodash、组件库这类大体积包的项目是直接的体积收益。
另一个容易被忽视但影响很大的变化是:Webpack 5 不再自动为 Node.js 核心模块注入 polyfill。Webpack 4 时代,只要代码里不小心 require('crypto'),打包器就会默默塞入一大坨兼容代码,产物体积悄悄膨胀。现在则会直接报错提示,逼迫开发者显式声明到底要用哪个 polyfill 或者确认自己真的需要它。迁移老项目时这是最常见的报错来源,解决思路通常有两种:安装 crypto-browserify 之类的替代包并在 resolve.fallback 中声明,或者通过 alias 把无用的引用指向空模块。
module.exports = {
resolve: {
fallback: {
path: require.resolve('path-browserify'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
},
},
};从工程角度看,这个改动短期增加了迁移成本,长期是好事。它让产物的依赖边界变得清晰,也倒逼开发者审视代码里那些来历不明的 Node 依赖,前端包里塞一个完整的 Buffer 实现,多数情况下纯粹是浪费。
升级建议与小结
如果项目还在 Webpack 4,升级时建议分三步走:先处理 Node polyfill 的报错,把 fallback 配置补齐;再开启持久化缓存验证构建正确性,观察 CI 上的缓存命中率;最后再考虑引入模块联邦这类架构级能力。同时确认项目使用的一些老 loader 和插件是否兼容 Webpack 5 的插件 API,特别是自定义插件如果用到了已废弃的 hooks,需要改用新的 Compilation API。
总体来看,Webpack 5 的核心价值在于让构建体系从单纯的打包工具,演进为能够支撑多应用协作与增量构建的基础设施。持久化缓存解决的是开发体验的痛点,模块联邦解决的是架构层面的复用难题,而更严格的产物控制则让性能优化有了更明确的抓手。这些能力叠加在一起,构成了它长期存在并且依然被大量项目选中的理由。
Webpack 5Module Federation持久化缓存修改时间:2026-09-03 13:08:55