跳至主要内容

缓存工作原理

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

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

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

computation-hashing

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

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

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

cache

如果 Lerna 没有找到相应的计算哈希值,Lerna 会运行任务,并在任务完成后,获取输出和终端日志并将其存储在本地(如果配置了远程缓存,也会存储在远程)。所有这些都在透明地进行,因此您无需担心。

虽然从概念上讲这相当简单,但 Lerna 对其进行了优化,以使您的体验更好。例如,Lerna

  • 捕获 stdout 和 stderr 以确保回放的输出看起来相同,包括在 Windows 上。
  • 通过记住文件在何处回放来最大程度地减少 IO。
  • 在处理大型任务图时仅显示相关输出。
  • 提供解决缓存未命中问题的办法。以及许多其他优化。

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

cache

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

源代码哈希输入

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

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

我们可以定义更精确的配置,如下所示

注意:“{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"]
}
}
}
}

如果给定目标的 outputs 属性未在项目的 package.json 文件中定义,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 文档