Rush Stack商店博客活动
跳至主要内容

增量构建

Rush 的 **增量构建** 功能通过跳过已更新的项目来加快速度。 在这种情况下,“已更新”意味着

  1. 该项目已在本地构建,并且
  2. 其输入文件和 NPM 依赖项自那时起未发生更改,并且
  3. 如果该项目依赖于任何其他 Rush 项目,则这些项目也已更新,并且
  4. 命令行参数未更改。(例如,在 rush build 之后调用 rush build --production 将需要重新构建。)

默认情况下,“输入文件”是项目文件夹下的所有源文件,除了被 .gitignore 排除的文件;详细信息可以使用 rush-project.json 配置文件进行自定义。

此功能可以与 项目选择参数 结合使用,其中用户明确告诉 Rush 处理哪些项目。 增量构建会重用本地磁盘上的现有输出。 这与 Rush 的 构建缓存 功能形成对比,后者可以从云存储容器中获取先前构建的输出。

如何使用它

要查看增量构建的实际效果,只需运行 rush build 命令两次

rush install

# This might take several minutes...
rush build

# ...but the second time it finishes in just a few seconds.
rush build

本机 rush build 被硬编码为增量。(rush rebuild 是此命令的非增量变体。)如果您定义了自己的自定义 批量命令,您也可以通过在 command-line.json 配置文件中启用 "incremental" 选项来使其成为增量。

它是如何工作的?

您的项目的构建脚本(由 rushx buildnpm run build 调用)可能已经实现了其自己的增量优化。 例如,Heft 为各种任务维护多个缓存。 但是,即使 rushx build 对项目不做任何操作,也仍然需要为生成 Node.js 进程、评估 JavaScript 文件和比较各个文件的 timestamps 而付出相当大的开销。 假设所有这些操作在一个项目中只花费 500 毫秒。 如果您的单体仓库有 100 个项目,那么在所有内容都已更新的最佳情况下,这将需要 100 x 0.5 = **50 秒** 的计算量。

Rush 通过对仓库执行自己的全局分析来消除这种开销,一次完成即可——这样一来,对于已更新的项目,构建脚本根本不会被调用。 作为额外的优化,Rush 的增量分析依赖于文件哈希而不是 timestamps。 例如,如果您切换到不同的 Git 分支,然后切换回来,许多文件的 timestamps 可能会被更新,但只要源文件内容没有改变,Rush 的增量分析就不会受到影响。 文件哈希由 @rushstack/package-deps-hash 库管理。 哈希保存在诸如 <your-project>/.rush/temp/package-deps_<task-name>.json 之类的文件中。 检查此文件可以帮助您了解算法的运行方式。

实际上,增量分析有三种不同的行为

  • **没有增量优化:** 如果调用的 Rush 命令不是增量的 (**command-line.json** 中的 incremental: false),则每次操作都会被重新执行。

  • **输出保留:** 如果构建缓存被禁用 (**build-cache.json** 中的 "buildCacheEnabled": false),则 Rush 会检查输入文件自同一台本地机器上的上次构建以来是否已更改。 如果没有文件被修改,则 Rush 假设项目文件夹下的输出文件是最新更新的,并且该项目会被“跳过”,而不会执行任何操作。 请注意,如果手动篡改输出文件,则此假设很容易被破坏。

  • **缓存恢复:** 如果 构建缓存 被启用 (**build-cache.json** 中的 "buildCacheEnabled": true),则 Rush 会查询缓存提供程序以查看该项目之前是否已被构建。 缓存提供程序可以是云存储或本地磁盘缓存。 对于缓存命中,项目的输出文件会被删除,然后从缓存中恢复。

**可能的未来改进:** 在当前的实现中,当构建缓存被启用时,**输出保留** 策略永远不会被使用。 换句话说,项目的输出文件夹总是会被清理,然后从缓存中恢复,这在磁盘上的文件已经更新的情况下似乎效率低下。 将 **输出保留** 和 **缓存恢复** 方法结合起来是否会更有效?

工程上的挑战在于,当构建缓存被启用时,我们还需要写入缓存,这需要对输出的正确性有很高的信心。 **输出保留** 算法目前不验证输出文件的哈希值,也不检查是否存在多余或缺少的文件。 如果实现了这种验证,其运行时间必须快于 tarball 解压缩,而 tarball 解压缩已经是一个非常快的操作。

仅构建已更改的项目(不安全)

假设我们的单体仓库有以下项目

a sample monorepo

在上图中,圆圈代表本地项目,而不是外部 NPM 依赖项。 从 DC 的箭头表示 D 依赖于 C;这意味着在构建 D 之前必须先构建 C

假设在重新构建所有内容之后,我们对项目 B 下的一个源文件进行了更改。 项目 CD 依赖于 B,因此也需要构建

rush build --impacted-by B

我们可以调用

# This command will rebuild B, C, and D
rush build

但是,如果您知道对 C 的更改不会影响其 API 合同怎么办? 例如,您可能更新了按钮控件的颜色或错误消息中的某些文本。

--changed-projects-only 标志告诉 Rush 仅构建更改了文件的项目

rush build --only B

我们可以这样调用它

# This command will rebuild B (but ignore the effects for C and D)
rush build --changed-projects-only

--changed-projects-only 是“不安全”的,因为如果下游项目实际上确实需要重新构建,则可能会遇到错误。 此参数通过假设您比 Rush 更了解哪些项目真正需要构建来节省时间。 如果此假设不正确,您始终可以运行 rush build 来恢复正常状态。

另请参阅