Webpack 5 发布后带来了一系列底层改进,社区常把其中与增量构建、缓存共享和跨应用复用相关的演进统称为 Striving Universe 奋斗宇宙。它并不是某个单独的配置项,而是一张由多个优化点织成的网络。理解这个网络,关键在于把握三个方向:让每次构建只做必要的事、让不同构建产物之间可以互相协作、让资源处理不再依赖额外 loader。接下来会从配置和原理层面逐一展开。

先从最影响开发体验的构建速度说起。Webpack 4 时代,大型项目的冷启动和增量构建时间经常被诟病,原因在于每次构建都要从头解析模块、生成 chunk,缓存机制不够灵活。Webpack 5 将文件系统缓存作为一等公民引入,使得构建结果可以持久化到磁盘上。这一能力正是 Striving Universe 中追求极致效率的核心组成部分。
持久化缓存:让重复构建不再是负担
在 Webpack 5 中,可以通过 cache 配置启用持久化缓存。它的工作原理是在首次构建完成后,把模块的编译结果、代码生成结果以及依赖关系等中间产物序列化到本地目录。第二次构建时,Webpack 会先读取缓存,只有发生变化的文件才需要重新解析和编译。对于动辄上千模块的应用,这种机制可以把二次构建时间从几十秒甚至几分钟压缩到几百毫秒。
启用方式并不复杂,以下配置展示了几个关键点。通过 type: 'filesystem' 指定文件系统缓存,利用 buildDependencies.config 让配置文件变化时自动使缓存失效,避免因为改了配置却仍然使用旧缓存导致诡异问题。version 字段则用于手动控制缓存版本,当升级依赖或调整构建流程时可以主动更新。
const path = require('path');
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
cacheDirectory: path.resolve(__dirname, '.webpack_cache'),
version: 'v1'
}
};需要注意的是,持久化缓存并非银弹。对于 CI 环境,缓存目录需要合理设置,否则可能因为工作目录变更导致缓存失效。另外,如果项目中存在动态生成文件或依赖了外部脚本,建议通过 cache.buildDependencies 显式声明这些依赖,确保缓存键值准确。整体来看,持久化缓存是 Striving Universe 中成本最低、收益最直接的优化手段,团队在迁移 Webpack 5 后应优先尝试。
模块联邦:拆掉应用之间的墙
Striving Universe 的另一大支柱是模块联邦(Module Federation)。它改变了过去多个前端应用共享代码时必须借助 npm 包、微前端框架或 iframe 的局限。模块联邦允许一个构建产物在运行时动态加载另一个构建产物中暴露出来的模块,就像调用本地模块一样自然。
想象一个后台系统包含主应用和若干子应用,传统做法是每个子应用独立打包,主应用通过路由和权限系统切换。如果某个公共组件需要升级,所有子应用都得重新发布。模块联邦的思路则是让远程应用把 Button、Modal 等模块暴露出来,主应用在运行时拉取远程入口文件并按需加载。这样一来,公共代码可以单独维护、独立部署,主应用不需要在编译期感知远程模块的具体实现。
下面是一个最简单的远程端与宿主端配置。远程端通过 exposes 声明自己对外提供哪些模块,宿主端通过 remotes 指定远程入口地址。共享依赖通过 shared 配置来避免 React 等库被重复加载。
// 远程应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' }
}
})
]
};
// 宿主应用 webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true }
}
})
]
};模块联邦的引入让 Striving Universe 从单点构建优化走向了跨应用协作。但它也带来了新的复杂度,比如远程模块的版本兼容、加载失败时的降级处理、共享依赖的单例约束等。团队在落地时需要为远程入口地址配置环境变量,并在代码中做好错误边界和懒加载,否则线上故障排查会变得困难。
内置资源模块与更精细的 Tree Shaking
Webpack 5 之前的项目里,处理图片、字体、SVG 等资源通常需要配置 file-loader、url-loader 或 raw-loader。Striving Universe 将这些能力内建为资源模块(Asset Modules),通过 type 字段即可声明处理方式,不再需要额外安装 loader。
资源模块提供了四种类型:asset/resource 会单独输出文件并返回 URL,类似 file-loader;asset/inline 将资源转为 data URI 内联到产物中,类似 url-loader 的限制以内的情况;asset/source 直接以源码字符串形式导出;asset 则根据大小自动在 resource 和 inline 之间切换。以下配置展示了三种常见用法。
module.exports = {
module: {
rules: [
{
test: /\.png$/,
type: 'asset/resource'
},
{
test: /\.svg$/,
type: 'asset/inline'
},
{
test: /\.txt$/,
type: 'asset/source'
}
]
}
};除了资源处理,Webpack 5 的 Tree Shaking 也变得更加精细。它改进了对 ES module 静态结构的分析,能够识别更多无用导出的场景。例如当一个模块从某个库中只引入一个函数时,构建产物不再包含该库的所有副作用代码,前提是库本身在 package.json 中正确声明了 sideEffects。这种优化在大型第三方依赖场景中能显著减小产物体积,也是 Striving Universe 追求极致的体现之一。
实践中建议开发者尽量使用 ES module 语法书写业务代码,并为组件库和无副作用样式文件配置 sideEffects: false。但要注意,如果某些 CSS 文件或 polyfill 确实具有副作用,需要在数组中显式保留,否则可能被误删导致样式丢失或功能异常。
Striving Universe 落地时的注意事项
将 Webpack 5 的这些能力引入现有项目,不建议一次性全部开启。合理的顺序是:先升级到 Webpack 5 并启用持久化缓存,观察构建稳定性;再逐步替换旧的 loader 配置为资源模块;最后在架构需要时引入模块联邦。这样每一步都能独立验证收益,出现问题时也更容易定位。
另外要特别注意缓存目录的版本管理以及模块联邦远程入口的可达性。在 CI 或 Docker 构建环境中,缓存路径最好挂载到持久化磁盘,否则每次全新环境都会丢失缓存。对于模块联邦,建议使用环境变量注入远程地址,不要将开发环境的地址硬编码到生产配置中。
Striving Universe 并非官方标准术语,但用它来概括 Webpack 5 在性能、复用和资源处理上的整体演进是合适的。理解这些特性背后的设计意图,比记住配置项本身更有价值。当团队真正把缓存、模块联邦和内置资源模块组合起来时,前端构建流水线的效率和可维护性会获得明显提升。