ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Relay loadQuery 完整指南:用命令式预加载实现 render-as-you-fetch 数据获取

Relay loadQuery 完整指南:用命令式预加载实现 render-as-you-fetch 数据获取 前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载loadQuery是 RelayReact 数据驱动框架提供的命令式查询预加载 API它与usePreloadedQuery()配合实现官方推荐的 render-as-you-fetch边渲染边获取模式在路由跳转、点击等事件发生时提前发起 GraphQL 请求渲染时直接消费已就绪或进行中的数据。读完本文你将掌握loadQuery的全部参数语义、三种 fetchPolicy 的取舍、query reference 的生命周期管理以及其底层在 loadQuery.js 中的执行与去重原理能够在真实 Relay 应用中正确实现请求早于渲染的数据预加载方案。loadQuery是什么定位与设计目标loadQuery是 react-relay 包导出的顶层函数见 index.js专门用于配合usePreloadedQuery()钩子实现 render-as-you-fetch 模式。与useLazyLoadQuery()在组件渲染时才发起请求不同loadQuery允许你在任意事件回调路由导航、按钮点击、滚动触发等中提前启动数据请求让网络请求与组件渲染并行进行从而缩短用户感知的加载时间。官方文档同时给出了一个重要提醒loadQuery返回的 query reference 会向 Relay store 泄漏数据如果在其不再被引用时没有调用.dispose()这些数据将一直保留在 store 中无法被垃圾回收。因此官方更推荐优先使用useQueryLoader参见 use-query-loader.md它会在组件卸载或引用不再可达时自动帮你处理 dispose。基础用法在事件中预加载查询loadQuery的典型调用方式是在事件中调用而不是在组件顶层调用。以下是最基础的示例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() // 本例为了简洁省略了这一步。这个示例包含了三个要点事件驱动调用loadQuery应该在事件回调中调用而不是在组件渲染顶层执行否则会违反 React 渲染阶段的规则详见下文行为一节。获取到的 query reference 交给usePreloadedQuery()消费渲染层通过usePreloadedQuery(query, queryReference)从 Relay store 读取数据查询进行中时会触发 Suspense 挂起失败时抛出错误成功时返回查询结果完整的消费示例见 use-preloaded-query.md。手动释放query reference 使用完毕必须调用.dispose()否则数据会持续被 store 保留。与 useQueryLoader 组合的推荐写法官方推荐的更安全的组合是用useQueryLoader包装loadQuery由钩子负责 query reference 的自动清理源码见 useQueryLoader.jsimport type {AppQuery as AppQueryType} from AppQuery.graphql; import type {PreloadedQuery} from react-relay; const {useQueryLoader, usePreloadedQuery} require(react-relay); const AppQuery graphql query AppQuery($id: ID!) { user(id: $id) { name } } ; function QueryFetcherExample(props: Props) { const [queryReference, loadQuery, disposeQuery] useQueryLoader( AppQuery, props.initialQueryRef, /* 例如由路由提供 */ ); if (queryReference null) { return ( Button onClick{() loadQuery({id: 4})}点击显示姓名/Button ); } return ( Button onClick{disposeQuery}点击隐藏并释放该查询/Button React.Suspense fallbackLoading NameDisplay queryReference{queryReference} / /React.Suspense / ); } function NameDisplay({queryReference}) { const data usePreloadedQueryAppQueryType(AppQuery, queryReference); return h1{data.user?.name}/h1; }useQueryLoader返回的loadQuery回调在内部就是调用loadQuery并把结果存入 React state见 useQueryLoader.js同时通过undisposedQueryReferencesRef追踪所有未被提交的 query reference在新引用提交时统一释放旧引用组件卸载时清空全部剩余引用从根本上避免数据泄漏。参数详解loadQuery接收四个参数其中前三个为必需后两个可选environment类型IEnvironment。用于执行请求的 Relay Environment 实例。如果你在 React 组件内部发起请求通常应该使用useRelayEnvironment()获取的环境参见 use-relay-environment.md以保证与渲染上下文一致。从源码看loadQuery对该环境执行environment.check()、environment.retain()、environment.executeWithSource()等操作loadQuery.js因此环境实例的 store 与网络层配置直接决定预加载行为。query类型GraphQLTaggedNodegraphql模板字面量或PreloadableConcreteRequest预加载的具体请求。使用graphql模板字面量声明查询Relay 编译器会将其编译为 ConcreteRequest。或者使用可预加载的具体请求通过require引入name-of-query$Parameters.graphql文件获取。只有查询标注了preloadable指令时Relay 编译器才会生成$Parameters文件。这种形式允许在查询 AST代码尚未下载完成时就基于持久化查询 ID 先行发起网络请求。variables类型QueryTypeTQuery[variables]。包含查询所需的变量值对象必须与查询内部声明的 GraphQL 变量一一匹配。options可选可选配置对象包含以下键键类型默认值说明fetchPolicystore-or-network \| store-and-network \| network-onlystore-or-network决定是否复用本地缓存数据以及是否发送网络请求networkCacheConfigCacheConfig{force: true}网络层的缓存配置fetchPolicy的三种取值语义如下详见官方 fetch-policies.md 与 availability-of-data.md 指南store-or-network默认复用本地缓存数据且仅当查询的某些数据缺失时才发送网络请求。如果查询完全命中缓存则不发起网络请求。store-and-network复用本地缓存数据并且无论缓存是否缺失都会发送网络请求。network-only不复用任何本地缓存数据总是发送网络请求完全忽略 Relay store 中可能存在的缓存。networkCacheConfig网络层缓存配置。Relay 网络层可能额外维护一个查询响应缓存对完全相同的查询复用网络响应。若想完全绕过该缓存默认行为即是如此传入{force: true}。事实上从实现源码看无论你是否传入该选项loadQuery都会强制合并force: true见 loadQuery.jsconst networkCacheConfig { ...options?.networkCacheConfig, force: true, };这意味着loadQuery预加载的网络请求默认总是穿透网络层响应缓存避免复用过期响应你仍可通过该选项传入其他网络层配置项如poll、metadata等来定制行为。environmentProviderOptions可选类型TEnvironmentProviderOptions。传递给environmentProvider的选项对象用于 EntryPoint 体系中的prepareSurfaceEntryPoint.js场景——即由环境提供方决定最终使用哪个环境执行请求。Flow 类型参数TQuery应与指定查询的 Flow 类型对应。该类型可从自动生成的文件query_name.graphql.js中导入。TEnvironmentProviderOptionsenvironmentProviderOptions参数的类型。从类型定义看EntryPointTypes.flow.jsLoadQueryOptions还包含一个内部使用的__nameForWarning字段用于开发警告中的查询命名属于内部细节一般无需关注export type LoadQueryOptions { readonly fetchPolicy?: ?FetchPolicy, readonly networkCacheConfig?: ?CacheConfig, readonly __nameForWarning?: ?string, };返回值query referenceloadQuery返回一个 query reference其唯一稳定可用的属性是dispose释放 query reference 在 store 中的保留retention。调用后该 query reference 引用的数据可能被垃圾回收。文档特别强调返回值的精确格式是不稳定且极可能变化的。强烈建议不要使用返回值上的任何其他属性因为这类代码在升级 Relay 时极容易破坏。正确的做法是把loadQuery()的结果直接传给usePreloadedQuery()。不过从当前源码loadQuery.js 与 EntryPointTypes.flow.js可以看到 query reference 的实际内部结构理解它有助于把握生命周期属性说明dispose()释放查询数据并取消仍在进行中的网络请求releaseQuery cancelNetworkRequestreleaseQuery()仅释放查询数据解除 store 保留cancelNetworkRequest()仅取消仍在进行中的网络请求fetchKey每次调用loadQuery递增的唯一键确保每个 query reference 被独立求值fetchPolicy本次调用使用的 fetch policyid查询 ID持久化查询 ID 或缓存 IDisDisposed是否已释放getternetworkError网络请求失败时的错误对象gettername查询名称networkCacheConfig本次调用的网络层缓存配置source网络事件的 Observable 源无网络请求时为 undefinedvariables本次调用的查询变量environment执行请求的 Relay 环境行为与生命周期细节官方文档明确了loadQuery的三条核心行为前两条可以从源码中得到印证1. 数据写入 store 的时机loadQuery()传入查询时会获取数据传入预加载的具体请求preloadable concrete request时则会同时获取数据与查询定义。一旦查询和数据都可用数据就会被写入 store。这与preloadQuery_DEPRECATED不同——旧 API 只有在查询被传入usePreloadedQuery时才会把数据写入 store。2. 数据保留与垃圾回收loadQuery返回的 query reference 会被 Relay store保留retain从而防止其数据被垃圾回收。一旦你对 query reference 调用.dispose()它就不再被保留数据随即可能被回收。源码中对应environment.retain(operation)调用loadQuery.js返回的retainReference在releaseQuery中被dispose()。useQueryLoader之所以推荐正是因为它把释放这件事自动化了。3. 渲染阶段调用会抛错loadQuery()如果在React 渲染阶段被调用会抛出错误。useQueryLoader返回的loadQuery回调同样有此限制而disposeQuery也不应在渲染阶段调用。这是 React 的规则约束副作用发起网络请求、修改状态只能在事件回调或 effect 中发生。补充fetchKey 与独立求值每次调用loadQuery都会生成新的fetchKey全局自增见 loadQuery.js。这确保每个创建出来的 query reference 都会被独立求值——即使它们对应相同的查询与变量。具体来说它避免了这种场景第二次调用loadQuery试图重新拉取同一查询时usePreloadedQuery内部的 Suspense 缓存错误地复用了旧结果而不是重新求值新引用并触发必要的重新获取。补充环境不一致时的回退当 query reference 被传入与创建时不同的环境上下文中时usePreloadedQuery会发出警告并回退到在渲染时用新环境重新执行查询见 usePreloadedQuery.js该场景未来版本会升级为硬错误实践中应保持环境一致。源码级原理loadQuery 内部执行链路从 loadQuery.js 的实现可以还原loadQuery的完整执行链路这有助于深入理解其行为生成新fetchKey保证引用的独立求值。解析 fetchPolicy若未显式传入会根据请求类型推断默认值——live 查询或启用执行期 resolver 的查询默认使用store-and-network纯客户端查询无服务端操作可拉取使用store-only其余使用store-or-networkloadQuery.js。构造 operation 并保留通过createOperationDescriptor创建操作描述符随后environment.retain(operation)保留该操作对应的数据。检查缓存可用性若 fetchPolicy 允许命中缓存则调用environment.check(operation)仅当状态非available即缓存缺失时才执行获取loadQuery.js。这正是store-or-network语义的落地实现。两层去重网络层使用fetchQueryDeduped(environment, raw-network-request- identifier, ...)对原始网络请求去重——保证同一 (environment, identifier) 对同时只有一个活跃请求当查询 AST 尚未就绪时多次调用仍可共用同一个网络请求loadQuery.js。操作执行层再次使用fetchQueryDeduped对操作执行去重并为 Suspense 基础设施跟踪活跃操作状态避免对同一响应重复处理loadQuery.js。急切执行 ReplaySubject 重放与通常惰性的 Observable 不同loadQuery希望在调用时立即启动请求。实现上用ReplaySubject捕获急切执行期间发生的事件再通过返回的 Observable 将事件重放给订阅者loadQuery.js。这也是usePreloadedQuery能拿到source并复用网络事件、避免二次请求的关键。预加载查询的模块注册当传入PreloadableConcreteRequestkind 为PreloadableConcreteRequest时通过PreloadableQueryRegistry查找已加载的查询模块若模块尚未加载代码分割场景则立即启动网络请求并注册onLoad回调待模块加载完成后执行写 store 与执行操作loadQuery.js。这套设计回答了为什么 loadQuery 能比 useLazyLoadQuery 更早发起请求它绕过了渲染时机的限制在事件回调中就完成请求发起、缓存检查与 store 写入渲染时usePreloadedQuery只需从 store 读取即可。何时用 loadQuery何时用 useQueryLoader两者关系可总结为useQueryLoaderReact 钩子把 query reference 存在 state 中自动管理 dispose。适合大多数组件内按需加载的场景点击按钮、条件渲染查询。其loadQuery回调与disposeQuery回调都有防泄漏保证。loadQuery直接调用纯命令式函数不依赖 React 生命周期适合在路由层、EntryPoint、事件总线等非组件上下文中预加载。代价是你必须亲自管理.dispose()否则 store 中的数据会泄漏。官方建议能使用useQueryLoader时就优先使用它只有在需要脱离组件上下文预加载如路由切换、EntryPoint 的getPreloadProps中时才直接调用loadQuery并务必在不再使用 query reference 时调用.dispose()。相关参考消费端 APIusePreloadedQuery自动管理生命周期的钩子useQueryLoaderrender-as-you-fetch 完整指南Rendering Queries缓存复用策略Fetch Policies 与 数据可用性核心实现源码loadQuery.js、useQueryLoader.js、usePreloadedQuery.js、EntryPointTypes.flow.js赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Stellar标签组件完全手册30内置插件的创意用法与实战案例Stellar标签组件完全手册30内置插件的创意用法与实战案例 Stellar是一款综合型hexo主题集成了博客、知识库、专栏和笔记功能内置了30多种标前端开发工具Relay 的 loadEntryPoint以命令式预加载实现 render-as-you-fetch 模式Relay 的 loadEntryPoint以命令式预加载实现 render as you fetch 模式 loadEntryPoint 是 React R前端开发工具Relay 中 loadQuery 的完整指南render-as-you-fetch 数据预取与 query reference 生命周期管理Relay 中 loadQuery 的完整指南render as you fetch 数据预取与 query reference 生命周期管理 loadQue前端开发工具上一篇V3 Admin Vite面向企业级应用的现代化Vue3中台架构设计下一篇Basaran与ChatGLM-6B集成教程构建中文对话AI的完整流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表