5月27日 17:34

Rspack 的缓存机制是如何工作的?

Rspack 的缓存机制是提升构建性能的核心手段。Rspack 目前支持内存缓存(Memory Cache)和持久化缓存(Persistent Cache)两种类型,配合快照策略、构建依赖追踪、可移植缓存等机制,能够在开发调试和生产构建中显著缩短耗时。

内存缓存

内存缓存是 Rspack 最基础的缓存形式,将模块编译结果和依赖图保存在内存中,使得增量构建和 HMR 能够快速响应。

在开发模式下,Rspack 默认启用内存缓存:

javascript
module.exports = { cache: true }

也可以显式指定类型:

javascript
module.exports = { cache: { type: 'memory' } }

内存缓存的特点是速度极快,但进程退出后缓存即丢失。对于日常开发中的热更新场景,内存缓存已经足够,这也是 Rspack 在 HMR 性能上表现优异的原因之一。

在生产模式下,cache 默认为 false,即不启用任何缓存。

持久化缓存

持久化缓存将构建结果写入磁盘,使得下次启动时可以直接复用上一次的编译产物,而无需重新执行模块解析和代码转换。这对于大型项目的冷启动场景尤为关键。

基本配置

javascript
const path = require('path') module.exports = { cache: { type: 'persistent' } }

启用后,Rspack 默认将缓存存储在 node_modules/.cache/rspack 目录下。你也可以自定义存储位置:

javascript
module.exports = { cache: { type: 'persistent', storage: { type: 'filesystem', directory: path.resolve(__dirname, '.cache/rspack') } } }

构建依赖(buildDependencies)

buildDependencies 用于声明哪些文件的变更应当导致缓存失效。Rspack 会计算这些文件的哈希值,当哈希值发生变化时,持久化缓存自动失效。

javascript
module.exports = { cache: { type: 'persistent', buildDependencies: [__filename, path.join(__dirname, 'tsconfig.json')] } }

需要注意的是,与 webpack 不同,Rspack 默认不预设任何构建依赖项。如果你希望配置文件修改后缓存能正确失效,必须将配置文件路径加入 buildDependencies

缓存版本(version)

version 字段用于隔离不同配置的缓存。不同 version 值的缓存互不干扰,Rspack 会为每个版本生成独立的缓存目录。

javascript
module.exports = { cache: { type: 'persistent', version: '1.0.0' } }

当项目配置发生重大变更(如升级 loader 版本、修改 babel 配置等)时,更新 version 可以避免旧缓存导致的构建错误。需要注意的是,不要在配置不同的构建之间共享相同的 version 和 storage.directory,否则可能命中错误的缓存。

缓存清理(maxGenerations)

Rspack 通过 maxGenerations 控制缓存的存活周期。默认值为 1,意味着如果某条缓存在下一次编译中没有被使用,就会被清理。增大该值可以让缓存存活更多轮次:

javascript
module.exports = { cache: { type: 'persistent', maxGenerations: 5 } }

此外,Rspack 在启动时会自动清理超过 7 天未被访问的缓存目录,无需手动维护。

快照策略(snapshot)

快照策略决定了 Rspack 如何判断文件是否在上次构建后被修改,直接影响缓存验证的效率。

managedPaths

managedPaths 用于指定由包管理器管理的目录(默认包含 node_modules)。对这些路径下的文件,Rspack 通过 package.json 中的 version 字段判断是否变更,而不是逐文件计算哈希,从而大幅加速缓存验证。

javascript
module.exports = { cache: { type: 'persistent', snapshot: { managedPaths: [/node_modules/] } } }

immutablePaths

immutablePaths 用于指定内容不会变更的路径。一旦路径被标记为不可变,Rspack 在热重启时会跳过对这些文件的检查,直接认为缓存有效:

javascript
module.exports = { cache: { type: 'persistent', snapshot: { immutablePaths: [path.join(__dirname, 'dist')] } } }

unmanagedPaths

unmanagedPaths 用于排除不应被 managedPaths 规则覆盖的路径。如果你的 node_modules 中有通过 git submodule 等方式管理的包,可以将其加入此列表,让 Rspack 对这些文件采用更精确的哈希验证。

可移植缓存(portable)

可移植缓存是 Rspack 为 CI/CD 场景设计的能力。启用后,缓存序列化时会将绝对路径转换为相对路径,使得缓存可以在不同机器和操作系统之间共享。

javascript
module.exports = { cache: { type: 'persistent', portable: true } }

典型的应用场景是在 CI 环境中:先将缓存构建并上传为 artifact,后续的构建任务直接下载复用,无需每次从零开始编译。Windows、Linux、macOS 之间可以共享同一份缓存,不需要为每个平台维护独立的缓存副本。

需要注意的是,可移植模式下,项目目录外的文件会被转换为相对路径,在新环境中如果这些文件不存在,可能触发额外的重新编译。

只读缓存(readonly)

只读模式适用于 CI 场景中使用预热缓存的构建任务。启用后,Rspack 只从磁盘读取缓存,不会写入新数据:

javascript
module.exports = { cache: { type: 'persistent', readonly: Boolean(process.env.CI) } }

这在多构建任务共享同一份缓存 artifact 时特别有用,可以避免并发写入导致的缓存损坏。

从 webpack 迁移缓存配置

如果你从 webpack 迁移到 Rspack,缓存配置需要做以下调整:

  1. 缓存类型:将 cache.type: 'filesystem' 改为 cache.type: 'persistent'
  2. 构建依赖:将 buildDependencies 从对象格式 { config: [__filename] } 改为数组格式 [__filename]
  3. 缓存版本:将 webpack 的 cache.namecache.version 合并为 Rspack 的 cache.version
  4. 快照配置:将顶层的 snapshot 配置移入 cache.snapshot
javascript
// webpack 配置 module.exports = { cache: { type: 'filesystem', buildDependencies: { config: [__filename] }, name: 'my-app', version: '1.0' } } // Rspack 配置 module.exports = { cache: { type: 'persistent', buildDependencies: [__filename], version: 'my-app-1.0' } }

实际使用建议

开发环境:默认启用内存缓存即可满足需求。如果项目较大导致冷启动慢,可以同时开启持久化缓存。

生产构建:生产模式下缓存默认关闭。对于频繁构建的场景(如预发布环境),可以开启持久化缓存并将 buildDependencies 配置完整,确保配置变更时缓存正确失效。

CI/CD 环境:结合 portable: truereadonly: true,将构建产物缓存作为 pipeline artifact 上传和复用,可以显著减少 CI 构建时间。根据社区反馈,命中缓存后构建速度可以提升 2-3 倍。

缓存失效排查:如果遇到缓存未命中或构建结果异常,首先检查 buildDependencies 是否完整,其次确认 version 是否需要更新。Rspack 在 cache.profile: true 开启时会输出缓存统计信息,有助于定位问题。

标签:Rspack