跳到主要内容

缓存工作原理

在运行任何任务之前,Lerna 会计算其计算哈希值。只要计算哈希值相同,运行任务的输出就是相同的。

默认情况下,例如 lerna run test --scope=remixapp 的计算哈希值包括:

  • remixapp 及其依赖项的所有源文件
  • 相关的全局配置
  • 外部依赖项的版本
  • 用户提供的运行时值,如 Node 版本
  • CLI 命令标志

计算哈希

这种行为是可自定义的。例如,lint 检查可能仅依赖于项目的源代码和全局配置。构建可以依赖于编译库的 dts 文件,而不是其源代码。

Lerna 计算任务的哈希值后,会检查是否之前运行过这个完全相同的计算。首先在本地检查,如果本地缓存丢失,且配置了远程缓存,则在远程检查。

如果 Lerna 找到了该计算,它会检索并重放。Lerna 将正确的文件放在正确的文件夹中,并打印终端输出。从用户的角度来看,命令运行了相同的操作,只是快得多。

缓存

如果 Lerna 没有找到对应的计算哈希值,Lerna 会运行任务,完成后将输出和终端日志存储在本地(如果配置了,也会存储在远程)。所有这些都是透明发生的,因此您不必担心。

尽管从概念上看这相当直接,但 Lerna 对此进行了优化,以提供良好的用户体验。例如,Lerna:

  • 捕获 stdout 和 stderr,确保重放的输出看起来相同,包括在 Windows 上。
  • 通过记住文件在哪里重放来最小化 IO。
  • 在处理大型任务图时仅显示相关输出。
  • 提供排查缓存未命中的便利方法。以及许多其他优化。

随着工作空间的增长,任务图看起来更像这样:

缓存

所有这些优化对于使 Lerna 可用于任何非平凡的工作空间至关重要。只执行最少量的工作。其余的要么保持原样,要么从缓存中恢复。

源代码哈希输入

构建/测试应用程序或库的结果取决于该项目的源代码以及其直接或间接依赖的所有库的源代码。

默认情况下,Lerna 是保守的。例如,在运行 lerna run test --scope=remixapp 时,Lerna 将考虑 remixapp 目录中的所有文件以及 headerfooter 目录(remixapp 的依赖项)中的所有文件。这会导致不必要的缓存未命中。例如,我们知道更改 footer 的规范文件不会改变上述测试命令的结果。

我们可以按以下方式定义更精确的配置:

注意:"{projectRoot}" 和 "{workspaceRoot}" 是任务运行器支持的特殊语法,在命令运行时将被适当地内部插值。因此,您不应将 "{projectRoot}" 或 "{workspaceRoot}" 替换为固定路径,因为这会使您的配置不够灵活。

nx.json
{
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"prod": ["!{projectRoot}/**/*.spec.tsx"]
},
"targetDefaults": {
"build": {
"inputs": ["prod", "^prod"]
},
"test": {
"inputs": ["default", "^prod", "{workspaceRoot}/jest.config.ts"]
}
}
}

使用此配置,构建脚本将仅考虑 remixappheaderfooter 的非测试文件。测试脚本将考虑被测项目的所有源文件,以及其依赖项的非测试文件。测试脚本还将考虑工作空间根目录的 jest 配置文件。

运行时哈希输入

您的目标还可以依赖于运行时值。

nx.json
{
"targetDefaults": {
"build": {
"inputs": [{ "env": "MY_ENV_NAME" }, { "runtime": "node -v" }]
}
}
}

参数哈希输入

最后,除了源代码哈希输入和运行时哈希输入外,Lerna 还需要考虑参数:例如,lerna run build --scope=remixapplerna run build --scope=remixapp -- --flag=true 会产生不同的结果。

请注意,只有传递给 npm 脚本本身的标志会影响计算结果。例如,从缓存的角度来看,以下命令是相同的。

npx lerna run build --scope=remixapp
npx lerna run build --ignore=header,footer

换句话说,Lerna 不会缓存开发人员在终端中键入的内容。

如果您构建/测试/lint... 多个项目,每个单独的构建都有其自己的哈希值,并且将从缓存中检索或运行。这意味着从缓存的角度来看,以下命令:

npx lerna run build --scope=header,footer

与以下两个命令相同:

npx lerna run build --scope=header
npx lerna run build --scope=footer

缓存了什么

Lerna 在进程级别工作。无论用于构建/测试/lint/等的工具是什么,结果都会被缓存。

Lerna 在运行命令之前设置钩子来收集 stdout/stderr。所有输出都被缓存并在缓存命中时重放。

Lerna 还缓存命令生成的文件。文件/文件夹列表在项目的 package.jsonoutputs 属性中列出:

注意:"{projectRoot}" 和 "{workspaceRoot}" 是任务运行器支持的特殊语法,在命令运行时将被适当地内部插值。因此,您不应将 "{projectRoot}" 或 "{workspaceRoot}" 替换为固定路径,因为这会使您的配置不够灵活。

例如 packages/my-project/package.json
{
"nx": {
"targets": {
"build": {
"outputs": ["{projectRoot}/build", "{projectRoot}/public/build"]
}
}
}
}

如果在项目的 package.json 文件中没有为给定目标定义 outputs 属性,Lerna 将查看 nx.jsontargetDefaults 部分:

nx.json
{
...
"targetDefaults": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
"{projectRoot}/dist",
"{projectRoot}/build",
"{projectRoot}/public/build"
]
}
}
}

如果两者都未定义,Lerna 默认缓存仓库根目录的 distbuild

跳过缓存

有时您想跳过缓存。例如,如果您正在测量命令的性能,可以使用 --skip-nx-cache 标志跳过计算缓存检查。

npx lerna run build --skip-nx-cache
npx lerna run test --skip-nx-cache

其他配置

有关配置任务和缓存的其他方法,请参见相关的 Nx 文档