ARTICLE DETAIL

资讯详情

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

Relay Fetch Policies 完整指南:用 loadQuery 复用本地缓存数据的四种策略

Relay Fetch Policies 完整指南:用 loadQuery 复用本地缓存数据的四种策略 前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载Relayreact-relay允许开发者通过给loadQuery传入fetchPolicy参数精细控制查询请求在读取本地缓存Store与发起网络请求之间的取舍。本文以 Relay 19 版本文档为核心结合packages/react-relay/relay-hooks/loadQuery.js、packages/relay-runtime/util/RelayRuntimeTypes.js等源码实现系统讲解store-or-network、store-and-network、network-only、store-only四种策略的语义、默认值与底层执行流程帮助你在导航回退、预加载、局部更新等场景中写出既快又省流量的数据加载代码。一、Fetch Policy 是什么复用缓存的第一步Relay 将查询结果缓存在本地的内存 Store 中。重新渲染一个页面时是否应该复用这份缓存数据、是否需要再次请求服务器这两个问题由Fetch Policy抓取策略决定。文档给出的核心定义是传入loadQuery的fetchPolicy决定了是否应该从本地缓存满足查询是否应该发起网络请求从服务器抓取查询取决于该查询的数据在 Store 中是否可用。在源码层面Relay 把 Fetch Policy 定义为一组联合类型见 RelayRuntimeTypes.jsexport type FetchQueryFetchPolicy store-or-network | network-only; export type FetchPolicy FetchQueryFetchPolicy | store-and-network | store-only;其中FetchQueryFetchPolicy是fetchQuery这类非 React API 支持的子集而完整的FetchPolicy则是loadQuery、useLazyLoadQuery、refetch等 API 共用的完整集合。二、如何传入 fetchPolicy以 useQueryLoader loadQuery 为例复用本地缓存数据的第一步就是把fetchPolicy传给loadQuery函数。loadQuery通常由useQueryLoader返回的加载函数调用完整的查询加载与渲染流程见读取查询Fetching Queries章节。原文档给出的示例const React require(React); const {graphql} require(react-relay); function AppTabs() { const [ queryRef, loadQuery, ] useQueryLoader(HomeTabQuery); const onSelectHomeTab () { loadQuery({id: 4}, {fetchPolicy: store-or-network}); } // ... }loadQuery的 TypeScript 类型签名见 loadQuery.d.ts确认了这一点export function loadQuery TQuery extends OperationType, TEnvironmentProviderOptions extends EnvironmentProviderOptions Recordstring, unknown, ( environment: IEnvironment, preloadableRequest: GraphQLTaggedNode | PreloadableConcreteRequestTQuery, variables: VariablesOfTQuery, options?: LoadQueryOptions, environmentProviderOptions?: TEnvironmentProviderOptions, ): PreloadedQueryTQuery, TEnvironmentProviderOptions;fetchPolicy属于options即LoadQueryOptions中的可选字段调用时可以不传此时使用默认策略见下文第四节。useQueryLoader 内部如何透传 fetchPolicy在 useQueryLoader.js 中useQueryLoader返回的加载回调会原样接收调用方的options包括fetchPolicy与networkCacheConfig并将其转发给loadQueryconst queryLoaderCallback useCallback( (variables, options) { const mergedOptions options ! null options.hasOwnProperty(__environment) ? { __nameForWarning: options.__nameForWarning, fetchPolicy: options.fetchPolicy, networkCacheConfig: options.networkCacheConfig, } : options; if (isMountedRef.current) { const updatedQueryReference loadQuery( options?.__environment ?? environment, preloadableRequest, variables, mergedOptions, ); undisposedQueryReferencesRef.current.add(updatedQueryReference); setQueryReference(updatedQueryReference); } }, [environment, preloadableRequest, setQueryReference, isMountedRef], );这说明 fetchPolicy 是通过loadQuery的options一路传递到查询执行层的组件层不做任何改写。三、四种 Fetch Policy 语义详解fetchPolicy可以是以下任意一种取自原文档并补充说明Fetch Policy是否复用本地缓存是否发起网络请求适用典型场景store-or-network是仅当数据缺失或过期时才请求默认策略日常导航回退store-and-network是总是请求需要立即展示缓存并后台刷新network-only否总是请求必须拿到最新数据的强一致性场景store-only是从不请求纯本地数据 / 调用方自行负责抓取各策略的精确语义如下1.store-or-network默认复用本地缓存数据并且仅当该查询有任何数据缺失或过期时才发送网络请求。如果查询被完全缓存则不会发起网络请求。2.store-and-network复用本地缓存数据并且总是发送网络请求无论Store 中是否有缺失或过期的数据。典型效果先用缓存瞬间渲染不阻塞网络响应到达后再用最新数据刷新界面。3.network-only不复用本地缓存数据并且总是发送网络请求抓取查询完全忽略本地可能存在的缓存及其缺失/过期状态。适合对数据新鲜度要求苛刻的场景如余额、库存、权限状态。4.store-only只复用本地缓存数据并且从不发送网络请求抓取查询。在这种情况下抓取数据的责任落在调用方身上但该策略也可用于读取和操作完全本地化的数据如commitLocalUpdate写入的本地数据。四、默认值不止是store-or-network原文档明确指出默认情况下Relay 会尝试从本地缓存读取查询如果该查询的任何数据缺失或过期就会从网络抓取整个查询——这个默认策略即store-or-network。不过从源码看默认值并非一成不变。在 loadQuery.js 中const DEFAULT_FETCH_POLICY: FetchPolicy store-or-network; const DEFAULT_LIVE_FETCH_POLICY: FetchPolicy store-and-network; function getDefaultFetchPolicy(request: ConcreteRequest): FetchPolicy { if ( request.params.metadata.live ! undefined || isExecTimeResolversEnabled(request.operation) ) { return DEFAULT_LIVE_FETCH_POLICY; } if (request.params.id null request.params.text null) { // A client-only query using read-time resolvers has no server operation to fetch. return store-only; } return DEFAULT_FETCH_POLICY; }可以归纳出三条自动推导规则源码事实Live 查询params.metadata.live存在或运行时解析器exec-time resolvers查询默认使用store-and-network因为这类查询需要持续获得服务器端的最新数据纯客户端查询既无持久化id也无查询文本例如使用 read-time resolvers 的 client-only query默认使用store-only因为根本没有对应的服务器操作可供抓取其余普通查询默认使用store-or-network。另外当调用方显式传入fetchPolicy时以显式值为准providedFetchPolicy ?? DEFAULT_FETCH_POLICY。五、底层原理fetchPolicy 如何驱动检查缓存 执行请求loadQuery的核心执行逻辑集中在checkAvailabilityAndExecute见 loadQuery.jsfetchPolicy 正是通过它来决策的const checkAvailabilityAndExecute (concreteRequest) { if (providedFetchPolicy null) { fetchPolicy getDefaultFetchPolicy(concreteRequest); } const operation createOperationDescriptor( concreteRequest, variables, networkCacheConfig, ); retainReference environment.retain(operation); // store-only不发起任何网络请求直接返回 if (fetchPolicy store-only) { return; } // 只有 fetchPolicy 允许从 Store 满足且环境检查结果不是 available 时才抓取 const shouldFetch fetchPolicy ! store-or-network || // environment.check 可能通过 missing field handlers 触发 Store 更新 // 短路检查可以避免不必要的更新 environment.check(operation).status ! available; if (shouldFetch) { executeDeduped(operation, () { const networkObservable makeNetworkRequest( concreteRequest.params, operation, ); const executeObservable executeWithNetworkSource( operation, networkObservable, ); return executeObservable; }); } };这段代码揭示了四个重要实现事实store-only直接短路创建操作描述符并retain保留数据防止被 GC后直接返回完全不触碰网络层store-and-network与network-only总是走网络由于fetchPolicy ! store-or-network恒为真shouldFetch恒为真请求必然发起区别在于前者在渲染时仍可从缓存读到旧数据后者则不会store-or-network依赖environment.check()只有当check返回的状态不是available即数据缺失或过期时才请求网络网络请求与执行都会去重makeNetworkRequest与executeDeduped都通过fetchQueryDeduped按(environment, identifier)去重保证同一查询的并发调用只发一次请求见 loadQuery.js。同时注意loadQuery生成的PreloadedQuery对象会把最终确定的fetchPolicy一并返回fetchPolicy字段usePreloadedQuery在渲染时会据此判断是否需要挂起等待。六、Fetch Policy 与数据存在性Presence of Data选择 fetchPolicy 之前需要先理解 Store 中数据的存在性lifetime。这一主题在数据存在性章节有完整讨论这里提炼与 fetchPolicy 直接相关的要点查询首次抓取后、且该查询正在屏幕上渲染期间其数据通常存在于 Store 中从未抓取过的查询数据自然缺失——此时store-or-network必然触发网络请求。为了防止内存无限增长Relay 会运行垃圾回收Garbage Collection删除不再被任何组件引用的数据。数据被过早回收会破坏缓存复用因此Query Retention默认情况下使用useQueryLoader/usePreloadedQuery的组件在挂载期间会保留retain查询卸载后释放之后随时可能被 GC。如需在组件生命周期之外保留数据可显式调用environment.retain(queryDescriptor)用返回的disposable.dispose()释放。gcReleaseBufferSizeStore 内部维护一个释放缓冲release buffer在查询被释放后仍临时保留指定数量的查询便于回退导航时复用。默认值为 10设为 0 等价于不启用缓冲查询会立即被释放回收。const store new Store(source, {gcReleaseBufferSize: 10});gcScheduler可传入调度函数决定 GC 何时执行不传时默认使用resolveImmediate。function gcScheduler(run: () void) { resolveImmediate(run); } const store new Store(source, {gcScheduler});GC 配置通常由应用基础设施在RelayEnvironment层统一配置日常业务代码一般无需操心但理解它有助于解释为什么某些数据明明抓取过却仍然缺失。七、Fetch Policy 与数据过期性Staleness of Data即使数据存在于 Store 中还必须考虑其过期性staleness。这一主题在数据过期性章节有完整讨论其与 fetchPolicy 的联动关系如下默认情况下Relay 不认为 Store 中的数据过期无论缓存了多久除非数据被显式标记为过期使用数据失效 API或数据超过查询缓存过期时间queryCacheExpirationTime。全局失效在 updater 中调用store.invalidateStore()可使失效前写入的所有数据被视为过期任何查询下次求值时都会被强制重新抓取。局部失效在 updater 中对具体记录调用user.invalidateRecord()只有引用该记录的查询会被视为过期。订阅失效useSubscribeToInvalidationState([ids], callback)可在记录被标记过期时立即触发回调从而主动刷新当前可见视图。查询缓存过期时间可通过queryCacheExpirationTime配置单位为毫秒例如const store new Store(source, {queryCacheExpirationTime: 5 * 60 * 1000});未配置时过期检查只关注记录是否被失效。一个查询在以下情况会被判定为过期stale它可以用 Store 中的记录满足请求且自上次抓取以来的时间大于queryCacheExpirationTime或它包含至少一个已被失效的记录。过期检查发生在新的请求被发出时例如调用loadQuery。已经渲染过期数据的组件仍可继续渲染这些数据但任何本应由过期数据满足的新请求都会走向网络。这正是store-or-network在缓存过期后自动重新抓取这一行为背后的完整机制。八、其他支持 fetchPolicy 的 APIfetchPolicy并非loadQuery独有refetch函数在抓取并渲染不同数据Fetching and Rendering Different Data一节中讨论的refetch函数同样接受fetchPolicy允许在重新抓取片段数据时切换策略例如从store-or-network临时切到network-only强制刷新。fetchQuerypackages/relay-runtime/query/fetchQuery.js支持的是FetchQueryFetchPolicy子集即store-or-network | network-only——因为命令式fetchQuery不涉及渲染与缓存复用store-and-network与store-only两种策略对它没有意义。useLazyLoadQuery同样接受fetchPolicy并且底层走的是与loadQuery一致的getDefaultFetchPolicy推导逻辑。九、选型建议四种策略怎么选结合原文档语义与源码行为给出可落地的选型参考日常导航 / 页面回退默认的store-or-network即可。数据在则秒开数据缺失或过期则自动走网络行为最符合直觉。列表页 → 详情页 → 返回列表页保持store-or-network配合默认的gcReleaseBufferSize: 10释放缓冲返回时大概率命中缓存、免去重新加载。进入页面时需要立即展示且允许后台刷新store-and-network。缓存先渲染网络响应回来再更新体验近似 SPA 中的乐观刷新。强一致性数据余额、库存、权限network-only宁可等待也绝不展示过期数据。纯本地数据commitLocalUpdate写入store-only配合显式retain防止数据被 GC。live 查询 / 运行时解析器查询保持默认——Relay 会自动使用store-and-network源码见第四节。十、总结fetchPolicy是 Relay 缓存复用体系本指南所在的 Reusing Cached Data 章节的入口通过loadQuery(query, variables, {fetchPolicy})传入策略即可在读缓存与发请求之间做出精确取舍。其底层由environment.check()的状态判断、getDefaultFetchPolicy的默认推导、以及fetchQueryDeduped的请求去重共同实现loadQuery.js。理解store-or-network、store-and-network、network-only、store-only四种策略的语义再配合数据存在性GC 与 Retention和数据过期性失效 API 与缓存过期时间两个维度的理解你就能在导航回退、预加载、局部数据更新等真实场景中写出既快速又省流量的 Relay 数据加载代码。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Relay Fetch Policy 完全指南用 loadQuery 与四种 fetchPolicy 精准控制缓存复用与网络请求Relay Fetch Policy 完全指南用 loadQuery 与四种 fetchPolicy 精准控制缓存复用与网络请求 Relay 的数据获取是缓前端开发工具Relay 的 Fetch Policies数据获取策略完整指南从 store-or-network 到 store-only 的缓存复用实战Relay 的 Fetch Policies数据获取策略完整指南从 store or network 到 store only 的缓存复用实战 导读 本文前端开发工具Relay 18 缓存复用指南深入解析 Fetch Policies 四种取值与 store 数据可用性判定Relay 18 缓存复用指南深入解析 Fetch Policies 四种取值与 store 数据可用性判定 Relay 在应用运行过程中会把多次查询获取到的前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表