前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载loadQuery是 Relay 中用于实现 render-as-you-fetch边渲染边取数模式的核心命令式 API它在 React 渲染之外例如路由导航、点击事件主动发起 GraphQL 查询请求返回一个由 Relay Store 保留retain的 query reference再交由usePreloadedQuery在渲染阶段消费。本文以 React Relay v19 官方 API 文档为主体结合仓库源码逐层拆解loadQuery的参数语义、返回值的生命周期、与useQueryLoader/usePreloadedQuery的协作方式以及底层去重与保留机制帮助你写出既早发起请求又不泄漏 Store 数据的高质量代码。loadQuery是什么设计初衷与适用场景loadQuery是react-relay导出的顶层函数设计目标是与usePreloadedQuery()搭配实现render-as-you-fetch。与useLazyLoadQuery在组件挂载/渲染时才触发请求不同loadQuery允许你在 React 的渲染阶段之外如路由跳转、用户点击、EntryPoint 预加载尽早开始数据请求让网络请求与 UI 渲染并行进行从而缩短用户可感知的等待时间。其核心契约包含三点主动拉取并写入传入查询后立即取数待查询与数据都可用时把数据写入 Relay Store。保留retain数据返回的 query reference 会被 Relay Store 保留防止数据被垃圾回收。需要显式释放如果不再引用该 query reference 而没有调用.dispose()数据会泄漏在 Relay Store 中。因此官方文档明确建议能使用useQueryLoader时优先使用它因为该 Hook 会替你妥善调用 dispose。从源码注释看loadQuery.jsv19 中loadQuery与旧 APIpreloadQuery_DEPRECATED的行为差异已被明确强调loadQuery一旦拿到查询与数据就会把结果写入 Store而preloadQuery_DEPRECATED只有在查询被传给usePreloadedQuery之后才写 Store。这一差异在下面的“Behavior”小节会展开说明。基本用法与完整代码示例loadQuery签名如下参见 EntryPointTypes.flow.js 中LoadQueryOptions与 loadQuery.js 的实现loadQuery( environment: IEnvironment, preloadableRequest: GraphQLTaggedNode | PreloadableConcreteRequest, variables: Variables, options?: ?LoadQueryOptions, environmentProviderOptions?: ?EnvironmentProviderOptions, ): PreloadedQuery官方文档给出的最小示例const MyEnvironment require(MyEnvironment); const {loadQuery} require(react-relay); const query graphql query AppQuery($id: ID!) { user(id: $id) { name } } ; // 注意通常不应在模块顶层调用 loadQuery。 // 而应在事件回调中调用如路由导航、点击等。 const queryReference loadQuery( MyEnvironment, query, {id: 4}, {fetchPolicy: store-or-network}, ); // 稍后把 queryReference 传给 usePreloadedQuery() // 注意query reference 应当调用 .dispose()本例中省略了这一步。更完整的 render-as-you-fetch 用法loadQuery通常不会单独使用而是与usePreloadedQuery或useQueryLoader配合形成“事件触发预加载 → 渲染阶段读取”的完整链路。完整示例参见 usePreloadedQuery 文档 与 useQueryLoader 文档import type {AppQueryType} from AppQueryType.graphql; const React require(React); const {graphql, useQueryLoader, usePreloadedQuery} require(react-relay); const AppQuery graphql query AppQuery($id: ID!) { user(id: $id) { name } } ; type Props { initialQueryRef: PreloadedQueryAppQueryType, }; function NameLoader(props) { const [queryReference, loadQuery] useQueryLoader( AppQuery, props.initialQueryRef, /* 例如由 router 提供 */ ); return ( Button onClick{() loadQuery({id: 4})} disabled{queryReference ! null} Reveal your name! /Button Suspense fallbackLoading... {queryReference ! null ? NameDisplay queryReference{queryReference} / : null } /Suspense /); } function NameDisplay({ queryReference }) { const data usePreloadedQuery(AppQuery, queryReference); return h1{data.user?.name}/h1; }在这个模式中事件回调里调用loadQuery此处通过useQueryLoader返回的回调提前发起请求渲染阶段usePreloadedQuery读取 Store查询仍在进行则 suspend失败则抛错成功则返回与查询形状一致的数据对象Flow 类型也会根据 GraphQL Schema 推导例如{ user: ?{ name: ?string } }。参数详解environmentRelay Environment 实例请求在该 Environment 上执行。如果是在 React 组件内部发起请求建议使用useRelayEnvironment()获取当前上下文中的 Environment而不是自行构造。从 useQueryLoader.js 的实现可以看到Hook 内部正是通过useRelayEnvironment()获取环境并在调用loadQuery时传入。query要取数的 GraphQL 查询有两种指定方式使用graphql模板字符串如graphql\query AppQuery($id: ID!) { ... }或者使用preloadable concrete request——即通过 require 形如name-of-query$Parameters.graphql的文件获得。Relay 只有在查询被preloadable注解时才会生成$Parameters文件。传入 preloadable concrete request 的意义在于即使查询的 AST 尚未加载也可以先用参数params立即发起网络请求待查询模块加载完成后再执行操作。在 loadQuery.js 中可以看到这条分支当preloadableRequest.kind PreloadableConcreteRequest时若PreloadableQueryRegistry中还没有对应的模块会先用params直接发起原始网络请求再通过PreloadableQueryRegistry.onLoad(queryId, callback)等待模块加载完成后补建OperationDescriptor并执行操作。variables包含查询所需变量值的对象必须与查询内部声明的 GraphQL 变量匹配。缺失或类型不符的变量会导致请求失败或类型错误。options可选一个 options 对象包含以下字段字段默认值说明fetchPolicystore-or-network详见下文源码补充决定是否复用本地缓存、以及何时发起网络请求networkCacheConfig{force: true}网络层缓存配置对象fetchPolicy的三种取值store-or-network默认会复用本地缓存数据并且仅当查询的某些数据缺失时才发网络请求。若查询已被完整缓存则不会发网络请求。store-and-network会复用本地缓存数据并且总是发网络请求无论本地缓存是否缺失数据。network-only不会复用本地缓存数据总是发网络请求取数忽略 Relay 中任何本地缓存。更多细节参见 Fetch Policies 指南 与 Garbage Collection / 数据在场性指南。源码补充默认 fetch policy 并非写死。从 loadQuery.js 的getDefaultFetchPolicy可以看到默认值取决于请求类型若request.params.metadata.live ! undefinedLive 查询或启用了执行期 resolverisExecTimeResolversEnabled默认改为store-and-networkDEFAULT_LIVE_FETCH_POLICY若查询是仅客户端params.id与params.text均为空且使用读期 resolver则默认store-only不发网络请求其余情况才是store-or-network。也就是说fetchPolicy: store-or-network作为“默认值”是针对常规服务端查询而言Live 查询与客户端查询各有其默认策略。networkCacheConfig默认值为{force: true}。它包含网络层的缓存配置选项。需要注意网络层可能还有一层额外的查询响应缓存query response cache会对相同查询复用网络响应。若想完全绕过这层缓存这正是默认行为传入{force: true}即可。源码中该默认值被显式强制见 loadQuery.jsconst networkCacheConfig { ...options?.networkCacheConfig, force: true, };也就是说即使用户传入自定义的networkCacheConfigforce: true也会被强制合并进去确保默认绕过网络响应缓存。这个networkCacheConfig随后既作为网络请求参数也用于创建OperationDescriptorcreateOperationDescriptor(concreteRequest, variables, networkCacheConfig)。environmentProviderOptions可选传给prepareSurfaceEntryPoint.js中environmentProvider的选项对象见 EntryPointTypes.flow.js 中EnvironmentProviderOptions {readonly [string]: unknown, ...}。它会原样保存在返回的 query reference 上environmentProviderOptions字段供environmentProvider.getEnvironment(options)使用。返回值Query Reference 及其生命周期loadQuery返回一个query reference内部类型为PreloadedQuery见 EntryPointTypes.flow.js关键属性如下dispose()释放该 query reference 在 Store 中的保留retain此后其引用的数据可能被垃圾回收。这是官方文档承诺、也最需要开发者关心的方法。官方文档特别提醒返回值的确切格式不稳定、极可能在未来版本中改变。强烈建议不要使用返回值的任何其他属性否则升级 Relay 版本时代码很可能损坏。正确做法是把loadQuery()的结果整体传给usePreloadedQuery()。从源码看 query reference 的内部结构虽然官方不建议直接访问其他属性但理解内部结构有助于把握生命周期。从 loadQuery.js 可以看出返回对象包含dispose()幂等地执行“释放数据 取消网络请求”内部调用releaseQuery()与cancelNetworkRequest()并通过isDisposed标志防止重复执行releaseQuery()释放 retainretainReference.dispose()幂等isReleased标志cancelNetworkRequest()根据是否已开始执行网络源退订执行订阅或网络订阅并取消onLoad回调fetchKey每次调用loadQuery递增的自增 key见源码let fetchKey 100001; fetchKey用于保证每次调用生成的 query reference 都被独立评估避免usePreloadedQuery的 Suspense 缓存错误地复用上一次的查询结果fetchPolicy、networkCacheConfig、environment、environmentProviderOptions、id、name、variables、source若发起了网络请求则为一个可重放网络事件的 Observable、networkError网络错误可被读取、isDisposed只读 getter等字段。这些内部字段正是usePreloadedQuery在渲染阶段恢复请求与执行策略的数据来源参见 usePreloadedQuery.js 中对fetchKey、fetchPolicy、source、environment的使用。BehaviorloadQuery的实际行为与边界约束取数并立即写入 StoreloadQuery()传入普通查询时立即取数传入 preloadable concrete request 时并行取数与加载查询。一旦查询与数据都就绪数据即被写入 Store。这与preloadQuery_DEPRECATED不同旧 API 只有在查询被传给usePreloadedQuery后才写 Store。也就是说loadQuery的“数据准备”行为更彻底即使 query reference 尚未被任何组件消费数据也已在 Store 中可用。保留与垃圾回收loadQuery返回的 query reference 会被 Relay Store保留通过environment.retain(operation)见 loadQuery.js防止其数据被垃圾回收。一旦调用.dispose()保留被释放数据可能liable to被垃圾回收。由于“泄漏”风险真实存在官方强烈建议优先用useQueryLoader。从 useQueryLoader.js 的实现可以看到Hook 用undisposedQueryReferencesRef维护所有已创建但未释放的 query reference每当新的 query reference 提交commit时会遍历并释放所有“早于当前提交”的引用组件卸载时也会全部释放。它还区分了 Live 查询调用dispose()与普通查询调用releaseQuery()只释放数据、保留网络请求语义非常细致。不能在 React 渲染阶段调用loadQuery()若在 React 的渲染阶段render phase被调用会抛出错误。它应当被放在事件回调、Effect 或路由/EntryPoint 等非渲染上下文中调用。useQueryLoader返回的loadQuery回调同样遵守此约束。底层机制急切执行、去重与可重放从 loadQuery.js 的实现可以还原loadQuery的几个关键底层设计急切执行eager execution与通常“订阅后才开始执行”的 Observable 不同loadQuery在调用时就同步发起请求。源码用ReplaySubject捕获急切执行期间发生的事件再通过Observable.create返回一个可重放replay这些事件的 Observable见executionSubject/returnedObservableloadQuery.js。这样后续订阅者如usePreloadedQuery能拿到从请求开始到订阅时刻的全部事件。双层去重原始网络请求去重以raw-network-request- getRequestIdentifier(params, variables)为标识调用fetchQueryDeduped保证同一 (environment, 请求标识) 只有一个网络请求在途。即使查询 AST 尚未加载、loadQuery被多次调用网络请求也不会重复见 loadQuery.js。操作执行去重以operation.request.identifier为标识再次fetchQueryDeduped保证对同一操作的执行只处理一次响应同时为 Suspense 基础设施跟踪操作的活动状态见executeDedupedloadQuery.js。store 命中短路当fetchPolicy为store-or-network且environment.check(operation).status available数据完整可用时直接跳过网络请求见checkAvailabilityAndExecuteloadQuery.js。Preloadable 分支若传入的是PreloadableConcreteRequest且查询模块已注册PreloadableQueryRegistry.get(queryId)非空则立即执行否则先发网络请求再注册onLoad回调等模块就绪后补齐执行见 loadQuery.js。与preloadQuery_DEPRECATED的对比仓库中保留了旧 API preloadQuery_DEPRECATED.js两者核心差异维度loadQuerypreloadQuery_DEPRECATED写入 Store 时机查询与数据就绪即写只有查询传给usePreloadedQuery后才写返回类型PreloadedQueryInner含dispose/releaseQuery/cancelNetworkRequestPreloadedQueryInner_DEPRECATED含status去重/缓存fetchQueryDeduped 网络层去重pendingQueriesByEnvironmentMap 30 秒过期清理DEFAULT_PREFETCH_TIMEOUT默认 fetchPolicy按请求类型动态选择固定store-or-network对调用者的要求必须自行dispose()或交给useQueryLoader同样需要由消费方管理生命周期旧 API 通过pendingQueriesByEnvironmentWeakMap 按 Environment 分组以 cacheKeygetRequestIdentifier(params, variables)拼接可选fetchKey去重并在订阅取消或请求完成后延迟 30 秒清理缓存条目这些实现细节在新 API 中被fetchQueryDeduped取代。常见错误与最佳实践小结在渲染阶段调用loadQuery会抛错。请把调用放到事件回调、useEffect或路由/EntryPoint 预加载逻辑中。忘记dispose()query reference 持续被 Store 保留数据泄漏。要么手动在不再需要时调用dispose()要么优先用useQueryLoaderHook 会在新引用提交、组件卸载时自动释放。顶层调用不要在模块顶层无条件调用loadQuery应让它在响应事件路由导航、点击等时触发。直接操作返回值的其他字段返回格式不稳定未来版本可能变化。只应把结果整体传给usePreloadedQuery()若确实需要 dispose只依赖dispose()。混淆 Store 缓存与网络缓存fetchPolicy控制的是 Relay Store 层是否复用数据networkCacheConfig控制的是网络层可能存在的 query response cache。默认{force: true}会绕过网络层响应缓存。延伸阅读usePreloadedQuery 文档消费 query reference 的 Hook 完整 API。useQueryLoader 文档自动管理 query reference 生命周期的推荐入口。Fetch Policies 指南 与 数据在场性指南fetchPolicy与垃圾回收的底层语义。源码loadQuery实现见 loadQuery.js相关类型见 EntryPointTypes.flow.js测试用例见 loadQuery-test.js 与 loadQuery-source-behavior-test.js。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Relay loadQuery 完全指南以命令式预取实现 render-as-you-fetchRelay loadQuery 完全指南以命令式预取实现 render as you fetch loadQuery 是 react relay 提供的一个命前端开发工具Relay loadQuery 完整指南用命令式预加载实现 render-as-you-fetch 数据获取Relay loadQuery 完整指南用命令式预加载实现 render as you fetch 数据获取 loadQuery 是 RelayReact前端开发工具Relay 的 loadQuery 命令式数据预取 API 全解配合 usePreloadedQuery 实现 render-as-you-fetchRelay 的 loadQuery 命令式数据预取 API 全解配合 usePreloadedQuery 实现 render as you fetch loa前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考