
Relay GraphQLSubscriptionConfig 类型完全指南构建实时数据订阅的配置核心【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relayGraphQLSubscriptionConfig 是 RelayJavaScript 数据驱动 React 应用框架中用于声明 GraphQL 订阅的配置对象类型它是requestSubscription命令式 API 与useSubscriptionReact Hook 两大订阅入口的统一参数契约。本文基于当前仓库 version-v13.0.0 文档 并结合 requestSubscription 源码 与 useSubscription 实现逐一拆解该类型的每个字段、底层行为与实战用法帮助你完整掌握如何在 Relay 应用中建立、接收与清理实时数据订阅。一、GraphQLSubscriptionConfig 是什么在 Relay 中GraphQL Subscription订阅是一种让客户端订阅服务器数据变化、并在数据变更时收到通知的机制。一个订阅在语法上与查询Query非常相似唯一的区别是使用subscription关键字subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { feedback_like_subscribe(data: $input) { feedback { id like_count } } }GraphQLSubscriptionConfig就是 Relay 中如何执行这个订阅的配置对象。根据仓库文档的定义它是一个具有以下字段的对象字段必填类型说明subscription是GraphQLTaggedNode通过graphql模板字面量声明的 GraphQL 订阅variables是Variables传递给订阅的变量cacheConfig否CacheConfig控制订阅请求缓存行为的配置onCompleted否() void订阅建立成功后执行的回调onError否(Error) {}发生错误时执行的回调onNext否(TSubscriptionPayload) {}收到新数据时执行的回调updater否SelectorStoreUpdater自定义更新本地 store 的函数在源码层面该类型定义于 packages/relay-runtime/subscription/requestSubscription.js#L44-L54其 Flow 类型签名如下export type GraphQLSubscriptionConfigTVariables, TData, TRawResponse Readonly{ configs?: ArrayDeclarativeMutationConfig, cacheConfig?: CacheConfig, subscription: GraphQLSubscriptionTVariables, TData, TRawResponse, variables: NoInferTVariables, onCompleted?: ?() void, onError?: ?(error: Error) void, onNext?: ?(response: ?TData) void, updater?: ?SelectorStoreUpdaterTData, };注意源码中还存在一个文档未列出的可选字段configs它允许以声明式配置DeclarativeMutationConfig如RANGE_ADD替代updater来更新 store并且源码通过warning提示二者不能同时提供详见 requestSubscription.js#L72-L75。二、核心字段详解1.subscription订阅本身subscription接收一个GraphQLTaggedNode即通过graphql模板字面量声明的订阅操作。它是配置中唯一必须包含完整 GraphQL 文档的字段const {graphql} require(react-relay); const feedbackLikeSubscription graphql subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { feedback_like_subscribe(data: $input) { feedback { id like_count } } } ;在 requestSubscription 源码 中第一件事就是校验该操作的合法性const subscription getRequest(config.subscription); if (subscription.params.operationKind ! subscription) { throw new Error(requestSubscription: Must use Subscription operation); }也就是说如果传入的graphql标签内容是 Query 或 Mutation会在运行时直接抛出requestSubscription: Must use Subscription operation错误。2.variables订阅变量订阅可以像查询或 Fragment 一样引用 GraphQL 变量。variables对象中的键值必须与订阅声明中的$variable一一对应。借助 Relay 的代码生成variables可以获得完整的 Flow/TypeScript 类型推断——订阅对应生成的*.graphql.js模块会导出输入类型例如FeedbackLikeSubscribeData。3.onCompleted订阅建立回调类型为() void在订阅成功建立时被调用。它不代表订阅结束而是代表服务器已确认订阅建立、数据流准备就绪。4.onError错误回调类型为(Error) {}当订阅过程中发生错误例如网络断开、服务器返回错误时被调用。5.onNext数据回调类型为(TSubscriptionPayload) {}每当收到一个新的订阅负载payload时被调用。TSubscriptionPayload是订阅负载的类型参数对应生成的__generated__/YourSubscription.graphql模块中导出的 Flow 类型。6.updaterstore 更新函数类型为SelectorStoreUpdater签名是(store: RecordSourceSelectorProxy, data) void它允许你命令式地直接读写 Relay store从而完全控制订阅负载落地到本地数据的方式可以创建全新的记录也可以更新或删除已有记录。常用于创建/删除记录、以及向 connection连接中添加或移除条目等复杂场景详见后文实战部分。三、CacheConfig控制订阅请求的缓存行为cacheConfig复用 Relay 通用的CacheConfig类型在订阅场景下用于控制请求的缓存与执行方式其字段如下字段类型说明forceboolean为true时无条件发起请求忽略任何已配置的响应缓存状态pollnumber以指定的毫秒间隔轮询实现实时更新该值会传给setTimeoutliveConfigIdstring通过调用 GraphQLLiveQuery 实现实时更新表示做 live query 时网关的配置metadataobject用户自定义元数据会随请求透传到网络层transactionIdstring用户提供的值用作某次操作执行实例的唯一标识在 requestSubscription-test.js 的测试中可以看到cacheConfig.metadata被透传到网络执行层的验证逻辑传入cacheConfig: {metadata}时缓存元数据被传递不传时则为undefined。在 requestSubscription.js#L64-L70 中cacheConfig会同subscription和variables一起被组装成操作描述符const {configs, onCompleted, onError, onNext, variables, cacheConfig} config; const operation createOperationDescriptor(subscription, variables, cacheConfig);四、两种消费方式useSubscription 与 requestSubscriptionGraphQLSubscriptionConfig是 Relay 订阅的两个入口共享的配置契约。1. 通过useSubscriptionHookReact 函数组件内useSubscription接收GraphQLSubscriptionConfig作为唯一必填参数import {graphql, useSubscription} from react-relay; import {useMemo} from react; const subscription graphql subscription UserDataSubscription($input: InputData!) { # ... } ; function UserComponent({id}) { // IMPORTANT: your config should be memoized. // Otherwise, useSubscription will re-render too frequently. const config useMemo( () ({ variables: {id}, subscription, }), [id, subscription], ); useSubscription(config); return /* ... */; }其行为完全继承自 useSubscription.js 实现组件挂载时发起订阅组件卸载时自动取消订阅useEffect返回dispose作为清理函数当environment、config或requestSubscriptionFn变化时取消旧订阅并使用新值重新订阅。从源码可以看到useSubscription本质上是requestSubscription的薄封装useEffect的依赖数组为[environment, config, actualRequestSubscription]——这正是文档反复强调config 必须用useMemo记忆化的原因若每次渲染都内联新建对象会导致依赖变化而反复重订阅造成不必要的网络请求与性能损耗。2. 通过requestSubscriptionAPI命令式需要命令式地发起订阅例如在事件回调、非组件代码中时使用requestSubscriptionimport {graphql, requestSubscription} from react-relay; const subscription graphql subscription UserDataSubscription($input: InputData!) { # ... } ; function createSubscription(environment) { return requestSubscription(environment, { subscription, variables: {input: {userId: 4}}, }); }它接收两个参数environment一个 Relay Environment与config即GraphQLSubscriptionConfig返回一个Disposable对象用于手动清理订阅type Disposable { dispose: () void, };从 requestSubscription.js#L86-L119 的源码可以看到它的完整执行链路用createOperationDescriptor构造操作描述符调用environment.executeSubscription({operation, updater})得到RelayObservable将onCompleted/onError/onNext分别挂到 observable 的complete/error/next上返回{dispose: sub.unsubscribe}作为 Disposable。其中onNext的实现值得注意收到响应后若负载带extensions.__relay_subscription_root_id会基于该 rootID 重建 selector然后通过environment.lookup(selector).data从本地 store 中读出归一化后的数据再交给onNext——这意味着订阅负载会被先写进 store再由onNext读取requestSubscription.js#L94-L114。五、实战从自动更新到 updater 精确控制1. 基础用法自动更新记录字段GraphQL Subscription 建立后当收到订阅负载时如果负载中的对象带有id本地 store 中对应 id 的记录会自动被负载中的新字段值更新。例如点赞订阅import type {Environment} from react-relay; import type {FeedbackLikeSubscribeData} from FeedbackLikeSubscription.graphql; const {graphql, requestSubscription} require(react-relay); function feedbackLikeSubscribe( environment: Environment, feedbackID: string, input: FeedbackLikeSubscribeData, ) { return requestSubscription(environment, { subscription: graphql subscription FeedbackLikeSubscription( $input: FeedbackLikeSubscribeData! ) { feedback_like_subscribe(data: $input) { feedback { id like_count } } } , variables: {input}, onCompleted: () {} /* Subscription established */, onError: error {} /* Subscription errored */, onNext: response {} /* Subscription payload received */, }); }服务器推送的负载示例{ feedback_like_subscribe: { feedback: { id: feedback-id, like_count: 321 } } }Relay 会根据id自动找到 store 中已有的Feedback记录并更新like_count任何订阅了该数据片段的组件都会自动收到通知并重新渲染。2. 进阶用法updater 向 connection 插入新记录当自动更新无法满足需求如创建/删除记录、向 connection 增删条目时提供updater获得完整控制权。以新评论创建时将其追加到评论列表为例import type {Environment} from react-relay; import type {CommentCreateSubscribeData} from CommentCreateSubscription.graphql; const {graphql, requestSubscription} require(react-relay); function commentCreateSubscribe( environment: Environment, feedbackID: string, input: CommentCreateSubscribeData, ) { return requestSubscription(environment, { subscription: graphql subscription CommentCreateSubscription( $input: CommentCreateSubscribeData! ) { comment_create_subscribe(data: $input) { feedback_comment_edge { cursor node { body { text } } } } } , variables: {input}, updater: store { const feedbackRecord store.get(feedbackID); // Get connection record const connectionRecord ConnectionHandler.getConnection( feedbackRecord, CommentsComponent_comments_connection, ); // Get the payload returned from the server const payload store.getRootField(comment_create_subscribe); // Get the edge inside the payload const serverEdge payload.getLinkedRecord(feedback_comment_edge); // Build edge for adding to the connection const newEdge ConnectionHandler.buildConnectionEdge( store, connectionRecord, serverEdge, ); // Add edge to the end of the connection ConnectionHandler.insertEdgeAfter(connectionRecord, newEdge); }, onCompleted: () {} /* Subscription established */, onError: error {} /* Subscription errored */, onNext: response {} /* Subscription payload received */, }); }理解这个updater的关键点updater的store参数是RecordSourceSelectorProxy实例可命令式读写 Relay store完整 API 参见 api-reference 的 store 文档订阅负载是 store 中的根字段root field记录需用store.getRootField(comment_create_subscribe)读取——根字段名即订阅中声明的顶层字段名任何由 updater 产生的本地数据变更都会自动通知订阅相关数据的组件并触发重新渲染。六、配置网络层让订阅真正跑起来GraphQLSubscriptionConfig只负责声明如何订阅真正与服务器建立长连接的是网络层。Relay 的网络层默认需要显式配置订阅的传输方式通常基于 WebSocket。在 graphql-subscriptions 指南 中展示了用graphql-ws客户端与Network.create组合的完整方案import {Network, Observable} from relay-runtime; import {createClient} from graphql-ws; const wsClient createClient({ url: ws://localhost:3000, }); const subscribe (operation, variables) { return Observable.create(sink { return wsClient.subscribe( { operationName: operation.name, query: operation.text, variables, }, sink, ); }); }; const network Network.create(fetchQuery, subscribe);其中Network.create(fetchQuery, subscribe)的第二个参数即为订阅执行函数Relay 会将它作为订阅请求的传输通道。也可以使用 legacy 的subscriptions-transport-ws只需注意将其 Observable 通过Observable.from(...)转换为 Relay 的RelayObservable类型。七、关键注意事项config 必须记忆化使用useSubscription时config 必须通过useMemo缓存否则每次渲染都会触发重订阅源码依赖[environment, config, ...]useSubscription.js#L46-L52。订阅必须用subscription关键字requestSubscription会在运行时校验 operation kind非订阅操作直接抛错。updater与configs二选一源码会发出Expected only one of \updater and configs to be provided 警告。自动更新有前提负载中对象带id时字段自动更新复杂更新增删记录、操作 connection必须走updater。类型参数TSubscriptionPayload应传入从自动生成的__generated__/YourSubscription.graphql模块导入的 Flow 类型如import type {UserDataSubscription} from ./__generated__/UserDataSubscription.graphql。需要复杂命令式控制时优先直接用requestSubscriptionAPIuseSubscription只是其薄封装。八、延伸阅读requestSubscription API 参考命令式订阅的完整行为说明useSubscription Hook 参考组件内订阅的生命周期行为GraphQL Subscriptions 指南包含网络层配置与更多实战示例CacheConfig 类型 与 SelectorStoreUpdater 类型本文两个嵌套类型的完整定义requestSubscription 源码GraphQLSubscriptionConfig的实际消费逻辑requestSubscription 测试含RANGE_ADD声明式配置、cacheConfig透传、onNextrootID 读取等行为的单元测试佐证【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考