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

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

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

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

配置输入条件

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 默认定义了两个输入集 defaultproduction ,每个输入集中会通过特定的语法定义文件的匹配规则。而每个规则中的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 还允许用户配置缓存的输入条件,灵活控制缓存的实效性。

相关推荐
斌味代码2 分钟前
el-popover跳转页面不隐藏,el-popover销毁
前端·javascript·vue.js
该怎么办呢3 分钟前
cesium核心代码学习-01项目目录及其基本作用
前端·3d·源码·webgl·cesium·webgis
踩着两条虫10 分钟前
AI 驱动的 Vue3 应用开发平台 深入探究(十九):CLI与工具链之Create VTJ CLI 参考
前端·ai编程·vite
嫂子的姐夫13 分钟前
040-spiderbuf第C8题
javascript·爬虫·python·js逆向·逆向
天下无贼!21 分钟前
【Python】2026版——FastAPI 框架快速搭建后端服务
开发语言·前端·后端·python·aigc·fastapi
GISer_Jing1 小时前
两种AI交互方式深度解析——浏览器书签&插件
前端·人工智能·ai·prompt
张张123y1 小时前
#Transformer架构与微调技术深度解析
深度学习·架构·transformer
哈__1 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-device-info
javascript·react native·react.js
SuniaWang1 小时前
《Spring AI + 大模型全栈实战》学习手册系列· 专题二:《Milvus 向量数据库:从零开始搭建 RAG 系统的核心组件》
java·人工智能·分布式·后端·spring·架构·typescript