ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Relay 部分缓存数据渲染(Partially Cached Data)实战指南:用 Fragment 边界与 Suspense 跳过加载态

Relay 部分缓存数据渲染(Partially Cached Data)实战指南:用 Fragment 边界与 Suspense 跳过加载态 前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载本指南围绕 Relay 的部分渲染partial rendering能力展开当查询仅有部分数据被本地缓存时如何立即渲染已缓存的内容而不是等待整个网络请求完成。你将在本文中学到 Fragment 如何充当部分渲染的边界、fetchPolicy与renderPolicy的配合方式以及如何用Suspense精确控制哪些 UI 区域需要挂起等待最终在真实页面中跳过不必要的加载状态。什么是部分渲染在 Relay 中渲染缓存数据时查询并不总是需要全有或全无。所谓部分渲染partial rendering指的是一个查询的某些字段可能已经在本地缓存中而另一些字段尚未获取——在这种情况下我们希望立即渲染已缓存的部分而不是等待整个查询的完整响应返回后再一次性渲染。这在尽快呈现页面的场景下尤其有价值。以个人资料页profile page为例用户在 App 使用过程中其name字段很可能早已被其他查询缓存到本地 Relay Store 中。那么当用户再次访问资料页时即使该页面其余数据尚未就绪只要name已缓存我们也希望立刻渲染出这部分内容跳过整页的加载态。Fragment 是部分渲染的天然边界部分渲染之所以可行依赖的是Fragment 组件具备挂起suspend能力详见 Loading States with Suspense。规则如下一个 Fragment 组件在渲染时如果它本地声明locally declared的数据存在缺失、且该数据正在被网络请求获取那么它就会 suspend它会一直挂起到它所需要的数据被获取完毕也就是它所属的父查询parent query完成获取为止。关键在于一个判定细节Relay 只把本地声明且缺失的数据视为缺失。也就是说Fragment spread 内部选择的数据不会影响外层查询或外层 Fragment 的缺失判定。这一条规则正是部分渲染能成立的根基——正是它保证外层组件可以在子 Fragment 数据缺失时先行渲染。一个完整的示例name 已缓存、username 缺失假设我们有这样一个 Fragment 组件它只关心用户的username/** * UsernameComponent.react.js * * Fragment Component */ import type {UsernameComponent_user$key} from UsernameComponent_user.graphql; const React require(React); const {graphql, useFragment} require(react-relay); type Props { user: UsernameComponent_user$key, }; function UsernameComponent(props: Props) { const user useFragment( graphql fragment UsernameComponent_user on User { username } , props.user, ); return (...); } module.exports UsernameComponent;再假设有一个查询组件HomeTab它查询了User的name字段并在内部通过 Fragment spread 包含上述组件/** * AppTabs.react.js * * Query Loader Component */ // .... const onSelectHomeTab () { loadHomeTabQuery({id: 4}, {fetchPolicy: store-or-network}); } // ... /** * HomeTab.react.js * * Query Component */ const React require(React); const {graphql, usePreloadedQuery} require(react-relay); const UsernameComponent require(./UsernameComponent.react); function HomeTab(props: Props) { const data usePreloadedQuery( graphql query HomeTabQuery($id: ID!) { user(id: $id) { name ...UsernameComponent_user } } , props.queryRef, ); return ( h1{data.user?.name}/h1 UsernameComponent user{data.user} / / ); }现在假设渲染HomeTab时我们此前只获取过{id: 4}这个User的name它已经存在于当前 Relay environment 对应的本地 Store 中而username从未被缓存。使用允许复用缓存的 fetchPolicy 渲染会发生什么如果用fetchPolicy: store-or-network或store-and-network来渲染该查询过程如下查询先检查自身本地数据是否缺失。在本例中HomeTabQuery只在本地直接选择了name字段而name在 Store 中是存在的所以查询本身没有缺失数据。注意Fragment spread即...UsernameComponent_user内部选择的username不参与外层查询的缺失判定。查询没有缺失数据于是立即渲染随后尝试渲染子组件UsernameComponent。UsernameComponent渲染UsernameComponent_userFragment 时Relay 发现username缺失。此时该 Fragment 组件会 suspend直到网络请求完成。这里要特别强调无论选择哪种fetchPolicy只要整个查询包括 Fragment 内部有任何数据缺失就一定会发起网络请求——fetchPolicy只决定是否复用缓存、何时请求并不决定缺数据时是否请求。问题悬浮会向上冒泡当UsernameComponent因username缺失而 suspend 时理想情况是name已缓存应该立刻渲染出来。但如果我们没有用Suspense边界去捕获这个挂起那么suspension 会一路冒泡导致整个App组件树全部进入挂起状态已缓存的数据也无法显示。用 Suspense 包裹 Fragment实现真正意义上的部分渲染要达成name可用即渲染即使username缺失的目标只需用Suspense把UsernameComponent包裹起来允许App的其他部分继续渲染/** * HomeTab.react.js * * Query Component */ const React require(React); const {Suspense} require(React); const {graphql, usePreloadedQuery} require(react-relay); const UsernameComponent require(./UsernameComponent.react); function HomeTab() { const data usePreloadedQuery( graphql query AppQuery($id: ID!) { user(id: $id) { name ...UsernameComponent_user } } , props.queryRef, ); return ( h1{data.user?.name}/h1 {/* Wrap the UserComponent in Suspense to allow other parts of the App to be rendered even if the username is missing. */} Suspense fallback{LoadingSpinner labelFetching username /} UsernameComponent user{data.user} / /Suspense / ); }加上这个Suspense边界后h1{data.user?.name}/h1立即渲染出已缓存的nameUsernameComponent在自己的Suspense内挂起展示fallback例如Fetching username加载指示网络请求完成后UsernameComponent恢复渲染页面无缝补全。嵌套 Fragment 同样适用上述机制对嵌套 FragmentFragment 内再包含 Fragment完全成立如果渲染某个 Fragment 所需的数据已在本地缓存无论其子 Fragment 或后代 Fragment 的数据是否缺失该 Fragment 组件都能正常渲染如果子 Fragment 数据缺失只需在子 Fragment 外层包一层Suspense就能让其他 Fragment 和页面其余部分继续渲染。这意味着开发者可以把部分渲染的粒度做到非常细沿着组件树逐层设置Suspense边界让每个已缓存的区块独立呈现。正如开篇的动机示例所言这让我们能够完全跳过加载状态渲染出与最终状态更接近的中间 UI从而显著改善感知性能。源码视角fetchPolicy 与 renderPolicy 如何驱动部分渲染部分渲染并非魔法其底层决策逻辑可以在源码中找到明确对应。类型定义在 RelayRuntimeTypes.js 中定义了与本文相关的核心类型export type FetchQueryFetchPolicy store-or-network | network-only; export type FetchPolicy FetchQueryFetchPolicy | store-and-network | store-only; export type RenderPolicy full | partial;FetchPolicy决定数据从哪里来、何时发请求RenderPolicy决定查询结果在数据不完整时是否允许渲染——full表示必须数据完整才渲染partial表示允许渲染部分数据。决策逻辑在 QueryResource.js 的_fetchAndSaveQuery中Relay 对每个 fetchPolicy 计算两个关键布尔值const queryAvailability environment.check(operation); const queryStatus queryAvailability.status; const hasFullQuery queryStatus available; const canPartialRender hasFullQuery || (renderPolicy partial queryStatus ! stale); switch (fetchPolicy) { case store-only: { shouldFetch false; shouldAllowRender true; break; } case store-or-network: { shouldFetch !hasFullQuery; shouldAllowRender canPartialRender; break; } case store-and-network: { shouldFetch true; shouldAllowRender canPartialRender; break; } case network-only: default: { shouldFetch true; shouldAllowRender false; break; } }从这段实现可以推断store-or-network仅当查询有数据缺失时才发起网络请求shouldFetch !hasFullQuery同时若renderPolicy为partial且数据非 stale则允许先渲染store-and-network无论缓存是否完整都会发起请求shouldFetch true但同样允许在部分数据可用时先行渲染network-only忽略本地缓存、总是请求并且shouldAllowRender false——数据到达前在查询根处挂起因此不存在部分渲染store-only绝不发起请求总是渲染 Store 中已有的数据。注释也印证了挂起行为shouldAllowRender为false时Relay 会为查询缓存一个 Promise使查询在根节点处挂起为true时则缓存查询资源并允许渲染继续见 QueryResource.js。如何设置 renderPolicyRenderPolicy通常通过UNSTABLE_renderPolicy选项传入查询 hooks例如useLazyLoadQuery的 options见 useLazyLoadQuery.js也可通过 environment 的默认策略UNSTABLE_getDefaultRenderPolicy配置见 QueryResource.js。由于它以UNSTABLE_前缀标记属于实验性 API使用时请留意版本更新与迁移说明。实际应用建议先判断查询根是否可渲染只有查询本地直接声明的数据非 Fragment spread 内部全部可用时查询根才会渲染。因此骨架数据如name、id应尽量放在查询本身而不是埋进子 Fragment。按需设置 Suspense 边界在每一个可独立呈现的已缓存区块外层包Suspensefallback 使用局部占位如骨架屏、小型 spinner避免整页 spinner。组合 fetchPolicy 使用store-or-network是尽量复用缓存的默认选择若希望数据一进缓存就立刻刷新后台同步可改用store-and-network对必须强制新鲜的数据使用network-only会牺牲部分渲染。完整的行为定义可参考 fetch-policies 与 use-lazy-load-query 文档。了解相关的缓存机制部分渲染依赖 Store 中的数据可用性判定与之配套的概念还包括数据的存在性presence-of-data、数据陈旧性staleness-of-data以及缺失数据的填充filling-in-missing-data它们共同决定了哪些数据可以被立即渲染。小结Relay 的部分缓存数据渲染本质上是Fragment 边界 Suspense 边界的组合Fragment 的缺失判定只看本地声明数据这让外层查询能在子 Fragment 数据缺失时先行渲染在子 Fragment 外层包Suspense把挂起范围限制在局部让已缓存部分立即呈现底层由fetchPolicy控制是否发请求、renderPolicy控制是否允许部分渲染二者在 QueryResource.js 中协同决策。掌握这套机制后你可以为高频访问页面如资料页、列表页构建数据到了多少就渲染多少的渐进式 UI最大限度减少加载态对用户体验的打断。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Relay 部分缓存数据渲染Partially Cached Data完全指南用 Fragment 边界与 Suspense 跳过加载态Relay 部分缓存数据渲染Partially Cached Data完全指南用 Fragment 边界与 Suspense 跳过加载态 本篇指南以 Re前端开发工具Relay 部分缓存数据渲染Partially Cached Data实战指南Fragment 边界与 Suspense 的正确搭配Relay 部分缓存数据渲染Partially Cached Data实战指南Fragment 边界与 Suspense 的正确搭配 本篇指南面向使用 R前端开发工具Relay 部分缓存数据渲染指南用 Suspense 与 Fragment 边界跳过加载态Relay 部分缓存数据渲染指南用 Suspense 与 Fragment 边界跳过加载态 本篇指南聚焦 Relay 框架中“部分缓存渲染”Partial前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表