Nx带来极致的前端开发体验——任务缓存

前言

前面我们讲过,为了提高项目的构建速度,社区将大部分的精力放到构建工具上,例如rspack、esbuild、swc等,利用语言优势提升构建速度。而像 webpack 这种老牌构建工具则将优化方向放在缓存上,但是他缓存的是构建流程中的中间结果,例如每个文件经过 loader 转换后的产物。

而本章节要介绍的任务缓存是指缓存任务执行之后的产物,例如构建或者测试任务,对于一个 package 来说,如果他的代码没发生改变,下一次执行 build 命令时可以直接读取上一次的构建产物,而无需再次进行构建,因为每次重复构建或者测试同一段代码的成本是非常昂贵的。

下面我们将专注于 Nx 的任务缓存机制,一起学习它的功能使用和原理实现。

定义缓存任务

使用 nx 创建项目时默认启用了任务缓存,开发者也可以在根目录的 nx.json 中全局配置任务缓存,也可以在每个 package 的 package.json 中单独配置。

jsx 复制代码
// nx.json
{
  "targetDefaults": {
        "build": {
            "cache": true
        }
        "test": {
            "cache": true
        }
    }
}

// package.json
{
    "name": "myreactapp",
    ...
    "targets": {
        "build": {
            "cache": true
        }
        "test": {
            "cache": true
        }
    }
}

以下面这个项目为例:

我们有一个 cache-test 项目,其依赖了 card 和 shop 这两个 package,当首次执行 cache-test 的 build 命令时,输出如下:

当修改 shop 中的代码再次执行cache-test 的 build 命令时,输出如下:

可以发现 card 并没有重新构建,而是读取的上一次构建的产物。除此之外我们分别在 shop 和 card 中添加了单测逻辑,并使用的 vitest,我们可以看下 test 任务的缓存逻辑是否生效(nx run-many --target=test),首次运行如下:

修改 card 的代码之后再次运行:

从输出结果可以看出,shop 读取的是缓存的结果。

缓存原理

在执行任何可缓存任务之前,比如 nx build myapp 或 nx test myapp,Nx 都会预先计算一段哈希值,这个哈希值表示了:**如果输入完全一样,那么输出也会完全相同。**也就是说,只要任务的所有输入条件都没变,Nx 就可以直接用之前的缓存结果(跳过实际执行),来极大加速构建或测试。

默认情况下,对于 nx test myapp 这种任务其计算哈希的输入会包括:

  • myapp 的源码以及其依赖项的源码。
  • 全局配置,比如 nx.json、tsconfig.base.json。
  • 第三方依赖版本。
  • 运行时环境,例如 node 版本。
  • 命令行参数

目前大部分 monorepo 项目管理工具都采用类似的策略实现任务缓存,例如rush.js、turborepo等。

配置输入条件

Nx 默认配置的输入条件是非常保守的,默认会把比较多的可能影响输出的内容算进去,避免漏掉导致复用过时结果,下面是 Nx 为 build 任务生成的默认输入条件:

jsx 复制代码
//nx.json

{
    "namedInputs": {
        "default": [
            "{projectRoot}/**/*",
            "sharedGlobals"
        ],
    "production": [
        "default",
        "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
        "!{projectRoot}/tsconfig.spec.json",
        "!{projectRoot}/src/test-setup.[jt]s"
    ],
    "sharedGlobals": []
    },
    // ...
    "targetDefaults": {
        "build": {
            "inputs": ["production", "^production"],
            "cache": true
        }
    }
}

namedInputs 中定义的是一些通用的输入集,nx 默认定义了两个输入集 default 和 production ,每个输入集中会通过特定的语法定义文件的匹配规则。而每个规则中的projectRoot 表示当前 package 的根目录。

在 targetDefaults 中配置了 build 任务的输入条件,第一个条件 production 很好理解,表示符合 production 输入集下的所有文件都作为输入内容。第二个 ^production 则表示其依赖的 package 中所有符合 production 输入集的所有文件都作为输入内容。

举个例子,我们希望将 *.md 文件从输入中排除,像 build 和 test 都不需要依赖 *.md 文件,为了实现这一点可以添加以下配置:

jsx 复制代码
//nx.json
{
	"namedInputs": {
        "production": [
            "default",
            "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
            "!{projectRoot}/tsconfig.spec.json",
            "!{projectRoot}/src/test-setup.[jt]s",
+           "!{projectRoot}/**/*.md"
        ]
    }
}

总结

在本章节中我们介绍了 Nx 的一个核心功能------任务缓存,任务缓存是通过直接缓存任务的最终输出结果来提升效率,这与构建工具的缓存机制有所不同,像 webpack 这种构建工具的缓存功能缓存的是构建过程的中间结果。

接着我们介绍了如何在项目中定义任务缓存,当我们使用 Nx 的插件生成代码时,Nx 会默认给任务自动配置缓存,并设置默认的缓存输入条件。除此之外 Nx 还允许用户配置缓存的输入条件,灵活控制缓存的实效性。

相关推荐
Jmyd01234 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
半个落月5 小时前
LangChain Agent 中间件实战(上):生命周期、状态、短路与工具包
javascript·langchain
砚凝霜5 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
半个落月5 小时前
LangChain Agent 中间件实战(下):DeepAgents文件权限、长期记忆与对话摘要
javascript·langchain
CCblandy6 小时前
令牌桶在 0.9 倍负载超发 19%、固定窗口边界放行 20 个:四种限流算法同构实测
后端·python·算法·性能优化·架构
唠点键盘之外的6 小时前
27 存量系统接大模型:旁挂一个 AI 微服务,老架构一行不改
人工智能·微服务·架构
u0111026757 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
GOWIN革文品牌咨询7 小时前
工业测量品牌系统化搭建指南:从内容架构到长期治理的全流程
大数据·人工智能·架构
jerrywus7 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
forest_097 小时前
Agent 系列02|Agent 架构全景图
人工智能·架构·prompt·agi