Relay Modern如何实现组件渲染时才拉取Fragment数据
Relay的Fragment合并是编译时行为:只要你把某个Fragment直接spread到当前执行的根查询里,编译阶段就会把Fragment内声明的所有字段,全部拼到最终发送的GraphQL请求文本中。
你的代码里QueryComponentQuery直接引用了FragmentComponent_country,编译出来的请求本身就带了continent.name字段,执行useLazyLoadQuery的时候自然会一次性拉取所有字段——这个过程和Fragment对应的组件有没有渲染完全无关,发请求的时候Relay根本不知道你后面会不会渲染这个组件,不存在自动检测渲染状态裁剪字段的内置逻辑。
根据你需要的延迟粒度选择对应实现即可:
方案1:配合
@skip指令绑定显隐状态,严格实现渲染才拉取
这是和你当前代码场景最匹配的改法,几乎不用调整组件结构。直接修改根查询,给Fragment spread加上@skip指令,用变量控制要不要把这部分字段放进请求:query QueryComponentQuery($id: ID!, $hideContinent: Boolean!) { country(code: $id) { name ...FragmentComponent_country @skip(if: $hideContinent) } }对应修改
useLazyLoadQuery的变量传参,把显隐状态传入:const data = useLazyLoadQuery<QueryComponentQuery>(query, { id: "US", hideContinent: !showContinent });改完之后,当
showContinent为false、不渲染Fragment组件时,请求会直接跳过continent相关字段,不会拉取冗余数据;点击按钮切到显示状态时,Relay会自动补拉缺失的continent字段,完全匹配按需拉取的需求。方案2:用
@defer做渐进式加载,不阻塞首屏
如果你不需要严格等用户触发渲染再拉数据,只是不想让非首屏字段拖慢首屏加载速度,给Fragment加@defer指令即可:query QueryComponentQuery($id: ID!) { country(code: $id) { name ...FragmentComponent_country @defer } }这种模式下,首屏请求会先拉取
name字段快速返回渲染,等主内容加载完,Relay会自动在后台发第二次请求拉取continent字段,用户点开的时候内容已经存在缓存里,交互体验更流畅。方案3:组件懒加载 + 查询自治
对于首屏出现概率极低的重型组件,直接用React.lazy动态引入组件,同时不要在父级根查询里引用它的Fragment,把数据查询逻辑完全写在组件内部。只有当组件代码被动态加载、准备进入渲染流程时,才会触发对应字段的请求,是最彻底的按需加载模式。
内容的提问来源于stack exchange,提问作者Joey Gough

