ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

urql 自动填充 Mutation 选择集:深入解析 @urql/exchange-populate 的 @populate 指令

urql 自动填充 Mutation 选择集:深入解析 @urql/exchange-populate 的 @populate 指令 前端【免费下载链接】urqlThe highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.项目地址https://gitcode.com/gh_mirrors/ur/urql点击查看免费下载urql/exchange-populate是 urql 生态中用于自动填充 Mutation 选择集selection set的官方 exchange。本文以 exchanges/populate/README.md 为主线结合仓库源码与测试用例系统讲解populateExchange的安装配置、populate指令用法、可调参数maxDepth、skipType以及底层实现原理帮助你在项目从 documentCache 向 Graphcache 演进时不再手动维护 Mutation 返回值中的每个字段。一、为什么需要自动填充 Mutation 选择集在 urql 的缓存体系里Mutation 返回什么字段直接决定缓存能否被更新。以 文档缓存document caching 为例当应用某处执行了# Query 1 { todos { id name } } # Query 2 { todos { id createdAt } }之后如果新增一个 Todo 的 Mutation 想同时刷新上述两个查询就必须手动把两个查询里出现的字段全部写进 Mutation 的返回选择集# 不使用 populate 的写法 mutation addTodo(id: ID!) { addTodo(id: $id) { id # 更新 Query 1 2 name # 更新 Query 1 createdAt # 更新 Query 2 } }随着应用规模增长追踪哪些查询请求过哪些字段会越来越困难——这正是populateExchange要解决的问题。根据 docs/advanced/auto-populate-mutations.md 的说明该 exchange 与 Graphcache 配合使用时尤其有价值它能在 Mutation 之后自动把此前查询观察过的字段补全从而让缓存数据自动保持最新。二、安装与快速开始1. 安装依赖populateExchange由独立的urql/exchange-populate包提供需要与urql或urql/core一同安装yarn add urql/exchange-populate # 或 npm install --save urql/exchange-populate从 exchanges/populate/package.json 可以看到该包以urql/core和wonka为依赖并要求项目自行安装graphql^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0与urql/core^6.0.0作为 peer 依赖。2. 注册 exchange将populateExchange加入createClient的exchanges数组即可import { createClient, cacheExchange, fetchExchange } from urql; import { populateExchange } from urql/exchange-populate; const client createClient({ url: http://localhost:1234/graphql, exchanges: [populateExchange({ schema }), cacheExchange, fetchExchange], });关键点populateExchange必须放在cacheExchange之前。原因有二见 docs/advanced/auto-populate-mutations.mdcacheExchange尤其是 Graphcache本身不认识populate指令需要先由populateExchange将其从文档中移除并替换为真实字段放在缓存前面可以避免不必要的重复工作让进入缓存层的操作已经是最终形态。3. 获取 schema 数据populateExchange的schema选项是后端 GraphQL Schema 的 introspection内省结果其类型为IntrospectionQuery见 populateExchange.ts 的类型定义。获取方式可参考 docs/graphcache/schema-awareness.md 中介绍的标准流程import { getIntrospectionQuery } from graphql; import fetch from node-fetch; // 或 Node.js 环境下你偏好的请求库 import * as fs from fs; fetch(http://localhost:3000/graphql, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ variables: {}, query: getIntrospectionQuery({ descriptions: false }), }), }) .then(result result.json()) .then(({ data }) { fs.writeFile(./schema.json, JSON.stringify(data), err { if (err) { console.error(Writing failed:, err); return; } console.log(Schema written!); }); });对于体积较大的内省结果还可以使用urql/introspection包的minifyIntrospectionQuery进行瘦身详见 docs/graphcache/schema-awareness.md该包与 populate 一样位于本仓库的 packages/introspection 目录中。三、核心用法populate指令注册完成后Mutation 里只需给字段加上populate指令exchange 就会自动补全此前所有查询中观察过的字段# 使用 populate 的写法 mutation addTodo(id: ID!) { addTodo(id: $id) populate }Note:上面两种 Mutation 最终发出的 GraphQL 请求完全一致。换句话说populate只是占位符在操作真正离开populateExchange之前它已经被展开为与手动写法等价的完整选择集——这一点在 populateExchange.test.ts 的快照测试中得到印证当查询过todos { id text creator { id name } }后addTodo populate会被展开为mutation MyMutation { addTodo { __typename id text creator { __typename id name } } }注意展开结果中还自动插入了__typename字段——这是为了让 Graphcache 能够确定实体的具体类型是 populate 展开时默认附带的。四、精细控制选择何时填充、如何限制填充1. 将populate放到更深层级如果不想填充整个 Mutation 响应以减小 payload可以把populate放在更靠下的字段上见 docs/advanced/auto-populate-mutations.mdmutation addTodo(id: ID!) { addTodo(id: $id) { id user populate } }此时只有user子选择集会被自动补全外层的id仍由你手动声明。这与源码中对每个带populate指令的字段单独展开的处理逻辑一致handleIncomingMutation会遍历文档逐字段检查是否带有populate指令命中才展开见 populateExchange.ts。2. 通过options限制填充范围populateExchange支持第二个参数options见 populateExchange.ts包含两个配置项配置项类型默认值作用skipTypeRegExp/^PageInfo\|(Connection\|Edge)$/匹配到该正则的类型名不会被自动填充字段默认跳过 Relay 分页相关类型maxDepthnumber2填充的最大嵌套深度防止无限递归或字段过多示例populateExchange({ schema, options: { maxDepth: 3, skipType: /Todo/, }, });从源码看这两个选项的默认值在创建 exchange 时被解析const maxDepth (options options.maxDepth) || 2;与const skipType (options options.skipType) || SKIP_COUNT_TYPE;populateExchange.ts。SKIP_COUNT_TYPE即/^PageInfo|(Connection|Edge)$/同文件第 82 行用于在默认情况下不展开 Relay 风格的PageInfo、Connection、Edge类型。在 populateExchange.test.ts 中maxDepth: 1时查询company { id employees { id todos { id } } }的展开结果只保留两层company → employees而employees下的todos不再展开skipType: /User/时User类型会被跳过、继续向更深的todos展开——这与直觉相反的行为正是跳过指定类型的含义。五、使用别名合并带变量的查询当多个查询对同一字段使用了不同的变量时需要借助 GraphQL 别名aliases才能让字段被正确合并见 docs/advanced/auto-populate-mutations.md。无效用法——同一字段todos带不同参数字段键冲突无法正确归并# Query 1 { todos(first: 10) { id name } } # Query 2 { todos(last: 20) { id createdAt } }配合别名的用法——用firstTodos/lastTodos区分开# Query 1 { firstTodos: todos(first: 10) { id name } } # Query 2 { lastTodos: todos(last: 20) { id createdAt } }Note:官方文档指出这一限制未来可能被放宽或移除。从源码可以理解这一限制的成因readFromSelectionSet在记录字段时会以字段名:序列化后的参数作为键存入typeFields见 populateExchange.ts。不同参数会生成不同的字段键而别名则天然避免了同名冲突让 exchange 能准确区分每一次字段观察。六、工作原理从观察查询到展开 Mutation结合 populateExchange.ts 的源码可以梳理出 exchange 的三条数据流其管线实现见 第 470-478 行监听查询handleIncomingQuery每当应用发起查询exchange 记录该 operation key并通过readFromSelectionSet沿选择集递归把每个字段及其所属类型、参数、子选择集存入内存中的typeFieldsMap第 428-460 行。同一文档中的 fragment 定义也会被收集进userFragments以便后续解析第 447-451 行。处理 teardownhandleIncomingTeardown查询被销毁时从活跃集合中移除。源码注释明确指出当前不会据此删除已记录的字段以避免缓存数据过期第 462-468 行。展开 MutationhandleIncomingMutation当操作是mutation且字段带有populate指令时先移除指令本身再根据字段的返回类型在typeFields中查找之前观察到的字段并递归补全第 132-343 行。展开过程中的几个关键细节抽象类型处理如果 Mutation 返回的是接口interface或联合union类型exchange 会为每个可能的实现类型生成带typeCondition的内联片段inline fragment并为每个片段注入__typename第 181-241 行。测试 populateExchange.test.ts 验证了removeTodo: [Node]接口与updateTodo: [UnionType]联合的展开结果。标量字段直接展开对象字段递归展开对GraphQLScalarType字段直接生成为普通字段节点对对象类型字段在depth maxDepth且未访问过该类型时递归填充子选择集第 243-318 行。参数保留查询中带参数的字段如createdAt(timezone: GMT1)会被原样记录并回填到 Mutation 中测试用例对此有快照验证populateExchange.test.ts。Fragment 支持查询中通过 fragment spread 观察到的字段同样会被展开populateExchange.test.ts而未使用的 fragment 不会被带入 Mutation同文件第 365-440 行。无记录可查时如果某个返回类型从未被查询观察过exchange 至少会补一个__typename字段保证缓存仍有可用的实体标识populateExchange.ts。七、实验性状态与已知限制需要说明的是populateExchange目前仍处于experimental实验性阶段见 docs/advanced/auto-populate-mutations.md 开头的说明例如 GraphQL 字段参数field arguments等部分用法尚未被完整覆盖exchange 也尚未经过大规模生产环境验证。使用时建议在集成测试中对populate展开后的请求做快照断言本仓库的 populateExchange.test.ts 提供了完整的参考写法可对照print(response.query)验证展开结果通过maxDepth、skipType控制展开规模避免过度填充在从 documentCache 向 Graphcache 迁移的过渡阶段用它作为桥梁逐步减轻手动维护 Mutation 选择集的负担。八、小结populateExchange为 urql 开发者提供了一种声明式、可自动维护的 Mutation 写法只需在字段上加一个populate指令exchange 便会基于此前所有查询的观察记录自动补全选择集并处理好__typename、抽象类型内联片段、参数保留、深度与类型限制等细节。配合 Graphcache 使用时它显著降低了Mutation 之后缓存数据过时的风险。相关实现与测试均可在本仓库的 exchanges/populate/src 目录中继续研读。赞分享前端【免费下载链接】urqlThe highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.项目地址https://gitcode.com/gh_mirrors/ur/urql点击查看免费下载相关推荐LangExtract 自定义输出 Schemaoutput_schema深度指南用 JSON Schema 锁定结构化提取结果LangExtract 自定义输出 Schemaoutput_schema深度指南用 JSON Schema 锁定结构化提取结果 导读 lx.extrac前端使用ChartJs.Blazor解决Blazor应用数据可视化难题的完整方案使用ChartJs.Blazor解决Blazor应用数据可视化难题的完整方案 在当今数据驱动的Web开发领域Blazor开发者面临着一个核心挑战如何在.NEurql批量操作优化使用populateExchange自动填充关联数据urql批量操作优化使用populateExchange自动填充关联数据 在开发GraphQL应用时你是否还在为手动编写冗长的mutation查询而烦恼是前端上一篇AndroidSVG核心功能解析从加载到渲染的完整流程下一篇出现amdgpu dkms failed for running kernel怎么办ROCm 6.2.1 驱动构建失败完整排查指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表