【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载本文基于 HowToGraphQLThe Fullstack Tutorial for GraphQL仓库中 React Relay 前端教程的 Subscriptions 章节content/frontend/react-relay/7-subscriptions.md完整讲解如何在 React 应用中通过 Relay Modern 的requestSubscriptionAPI 与subscriptions-transport-ws包接入 GraphQL 订阅实现其他用户投票时所有在线用户的票数字段无需刷新页面即可实时变化的效果。读完本文你将掌握 RelayEnvironment中订阅端点的配置方式、订阅查询与缓存updater的编写方法以及订阅的挂载位置选择与验证手段。GraphQL Subscriptions 是什么从请求-响应到事件流GraphQL 的三种操作类型中subscription 与 query、mutation 有着本质区别query 与 mutation 遵循请求-响应循环request-response-cycle客户端发一次请求服务端回一次响应连接即结束subscription 则代表一条数据流stream客户端向服务端订阅某类事件之后每当该事件在服务端真实发生时服务端都会主动把对应数据推送给客户端。这里的事件通常对应 mutation 引发的数据变更——数据的创建、更新或删除。在 HowToGraphQL 的 HackerNews 示例中具体目标就是当其他用户对某个 link 投票Vote被创建时当前页面显示的votes.count立即自增。订阅最常见的实现方式是 WebSocket服务端与已订阅的客户端之间维持一条长连接事件发生时通过该连接下发数据。这也是本教程前端使用wss协议端点的原因。Relay Modern 的订阅 APIrequestSubscription需要特别说明的是订阅能力直到 Relay ModernRelay 1.0才进入 Relay。在此之前Relay Classic 并不提供订阅支持。Relay Modern 提供了requestSubscription函数用于向服务端发起订阅。requestSubscription的使用方式与前面 mutation 章节中的commitMutation非常相似同样传入当前项目的environment实例同样可以提供一个updater回调指明收到服务端新数据后应如何更新 Relay 缓存Store同样支持onError回调处理错误。但有一点不同为了让requestSubscription真正工作必须改造 Relay 的Network——Network除了需要发起查询/变更请求的函数外还需要第二个函数专门负责知道订阅端点地址、能够建立并维持到订阅端点的连接。如果订阅基于 WebSocket该端点使用wss协议而非http(s)。后端前提Graphcool 的 Simple API 与 Relay API 之分本教程后端使用 Graphcool教程使用的是其 legacy 版本见 content/frontend/react-relay/1-getting-started.md 中的说明。Graphcool 为每个项目暴露两套类型定义略有差异的 GraphQL APISimple API提供所有模型类型的直觉式 CRUD 能力Relay API满足 Relay 对 GraphQL schema 的要求如Viewer、Node、Edge等类型。当时的限制是订阅只直接支持 Simple API。要在 Relay API 中使用订阅需要对你喂给relay-compiler的schema.graphql做手工调整在 schema 中补充Subscription类型定义。HowToGraphQL 已替学习者完成了这些调整教程第 3 章要求从https://graphqlbin.com/hn-relay-full.graphql下载 schema该文件里已经包含了手工添加的Subscription类型。你可以直接查看自己项目根目录下的schema.graphql中的Subscription类型确认它定义了形如Vote新增投票这样的订阅字段。第一步为项目引入 WebSocket 支持在 Relay 项目根目录执行yarn add subscriptions-transport-ws0.8.3注意教程特意锁定了0.8.3版本原因是该版本提供了SubscriptionClient这个 API教程原文注明后续会更新到最新版 API。SubscriptionClient实现了标准的 GraphQL over WebSocket 协议与 Graphcool 的 subscriptions API 协议兼容因此是这里的合适选择。第二步改造 Relay Environment注册订阅端点打开src/Environment.js把此前仅含一个查询函数的Network.create改造为接收两个函数的形式import { SubscriptionClient } from subscriptions-transport-ws // 1 const fetchQuery (operation, variables) { return fetch(https://api.graph.cool/relay/v1/__PROJECT_ID__, { method: POST, headers: { Accept: application/json, Content-Type: application/json, Authorization: Bearer ${localStorage.getItem(GC_AUTH_TOKEN)} }, body: JSON.stringify({ query: operation.text, variables, }), }).then(response { return response.json() }) } // 2 const setupSubscription (config, variables, cacheConfig, observer) { const query config.text const subscriptionClient new SubscriptionClient(wss://subscriptions.__REGION__.graph.cool/v1/__PROJECT_ID__, {reconnect: true}) subscriptionClient.subscribe({query, variables}, (error, result) { observer.onNext({data: result}) }) } // 3 const network Network.create(fetchQuery, setupSubscription)三段代码各自的职责fetchQuery与教程早期章节完全相同的查询/变更请求闭包只是从匿名函数抽成了具名变量以便与下面的函数一起传给Network.create。其中的__PROJECT_ID__需要替换为你 Graphcool 项目的实际 IDAuthorization头携带第 5 章Authentication 章节存入localStorage的令牌。setupSubscriptionNetwork用于与订阅端点通信的第二个函数。它接收config其中config.text就是订阅查询文本决定客户端关心什么事件、要接收哪些字段内部用SubscriptionClient建立并维持到wss://subscriptions.__REGION__.graph.cool/v1/__PROJECT_ID__的连接reconnect: true表示断线自动重连。每当服务端推送数据就通过observer.onNext({data: result})把结果交给 Relay 运行时后者再去执行你在requestSubscription里声明的updater。这里有两个占位符要替换__PROJECT_ID__为项目 ID__REGION__为你的 API 所在的 AWS 区域例如ap-northeast-1。Network.create(fetchQuery, setupSubscription)用两个函数创建Network随后照旧用于实例化 RelayEnvironment。关于占位符的查证方法教程给出的两条路径查看项目文件project.graphcool的 frontmatter第一行注释# project: id即项目 ID或在终端执行graphcool endpoints命令会输出 Relay API 与 Subscriptions API 等全部端点__REGION__的确认方法打开 Graphcool 控制台点击左下角Endpoints按钮查看Subscriptions API一行给出的完整地址例如wss://subscriptions.ap-northeast-1.graph.cool/v1/project-id把其中的区域名填回占位符即可。第三步编写 NewVoteSubscription 订阅封装延续本教程每种 mutation/subscription 都放在专门文件里、对外导出一个便捷函数的约定在src下新建subscriptions目录在其中创建NewVoteSubscription.js写入以下内容import { graphql, requestSubscription } from react-relay import environment from ../Environment const newVoteSubscription graphql subscription NewVoteSubscription { # 1 Vote { # 2 node { id user { id } link { id _votesMeta { count } } } } } // 3 export default () { const subscriptionConfig { subscription: newVoteSubscription, variables: {}, updater: proxyStore { const createVoteField proxyStore.getRootField(Vote) const newVote createVoteField.getLinkedRecord(node) const updatedLink newVote.getLinkedRecord(link) const linkId updatedLink.getValue(id) const newVotes updatedLink.getLinkedRecord(_votesMeta) const newVoteCount newVotes.getValue(count) const link proxyStore.get(linkId) link.getLinkedRecord(votes).setValue(newVoteCount, count) }, onError: error console.log(An error occured:, error) } requestSubscription( environment, subscriptionConfig ) }逐段拆解1. 订阅的根字段表达事件。Vote字段声明客户端关心Vote类型上发生的事件本例即有新的投票被创建。Graphcool 会为每个模型类型在Subscription类型下生成同名字段作为事件入口。2. payload 决定每次推送携带什么数据。node字段代表刚创建的Vote记录每次有人投票服务端都会推送新投票的id、投票者user.id以及被投链接的link.id和link._votesMeta.count_votesMeta是 Graphcool 在 Relay API 中为关系提供的元数据连接count即该链接当前的总票数——注意这里是服务端算好的绝对值不是增量。3. 导出函数封装requestSubscription。导出的默认函数可以在应用任何位置调用其内部组装subscriptionConfig并真正向服务端提交订阅。其中updater是订阅生效的关键它与第 6 章投票 mutation 中commitMutation的updater参见 content/frontend/react-relay/6-more-mutations-and-updating-the-store.md使用的是同一套 Relay Store 代理 APIproxyStore.getRootField(Vote)Vote是订阅的根字段订阅 payload 相对 mutation payload 多包了一层node所以要先getLinkedRecord(node)取出新投票再依次遍历link→_votesMeta用getValue(count)拿到标量票数proxyStore.get(linkId)按 Relay ID 取出缓存中的链接记录link.getLinkedRecord(votes).setValue(newVoteCount, count)将票数字段直接改写为服务端下发的最新值。由于第 6 章已在Link组件的 fragment 中查询了votes { count }此处改写会立即反映到所有渲染该字段的组件上无需任何手动重渲染逻辑。第四步挂载订阅——为什么放在 LinkList 而不是 Link订阅的调用位置看似无关紧要它没有依赖组件上下文的变量参数但有一个硬约束订阅只能被发起一次。如果把它放进Link组件页面上渲染多少条链接就会发起多少条相同的 WebSocket 订阅既浪费连接又造成重复推送。因此教程把它放进只挂载一次的LinkList组件中。打开src/components/LinkList.js添加import NewVoteSubscription from ../subscriptions/NewVoteSubscription componentDidMount() { NewVoteSubscription() }componentDidMount保证组件进入 DOM 后才发起订阅且LinkListPage路由下该组件只会挂载一次从而满足仅订阅一次的约束。第五步编译 GraphQL 代码并验证由于NewVoteSubscription.js中新增了带graphql标签的订阅文档必须重新运行 Relay Compiler 让它通过 schema 校验并生成编译产物relay-compiler --src ./src --schema schema.graphql说明schema 文件就是教程第 1 章中用get-graphql-schema从 Relay API 端点下载、并包含手工添加的Subscription类型的schema.graphql。若编译器在此报未知字段 Vote通常说明你用的还是未调整的 schema。然后运行yarn start启动应用。教程推荐的验证方式是开两个浏览器窗口或标签页同时运行该应用在一个窗口中对某条链接投票另一个窗口中该链接的票数字应立即自动 1全程不刷新页面。这正是订阅链路完整跑通的表现——WebSocket 推送 →setupSubscription中的observer.onNext→ Relay 执行updater→ Store 变更 → 组件重新渲染。小结与常见排查点把本章节放入整个 React Relay 教程的脉络中看章节结构见 meta/structure/react-relay.md环节关键 API / 命令说明WebSocket 客户端subscriptions-transport-ws0.8.3的SubscriptionClient教程锁定 0.8.3 以获得SubscriptionClientAPI环境配置Network.create(fetchQuery, setupSubscription)第二个函数负责建立并维持wss订阅连接订阅发起requestSubscription(environment, config)与commitMutation同构支持updater/onError缓存更新proxyStore.getRootField(Vote)→getLinkedRecord→setValue订阅 payload 比 mutation 多一层node编译relay-compiler --src ./src --schema schema.graphql每次修改graphql标签代码后必须执行验证双窗口投票测试一个窗口投票另一窗口票数即时变化几个容易踩坑的点占位符未替换__PROJECT_ID__与__REGION__必须替换为graphcool endpoints输出中的真实值region以控制台 Endpoints 面板中 Subscriptions API 一行为准schema 缺少Subscription类型Relay API 需要手工调整 schema 才能通过 compiler 校验教程已通过graphqlbin.com/hn-relay-full.graphql预置订阅被重复发起确认订阅调用只出现在单例挂载的组件本例为LinkList.componentDidMount中页面看不到更新先检查onError是否打印了错误再确认Link组件的 fragment 确实查询了votes { count }——只有被查询过的字段参与缓存失效与重渲染。最后留一个与原文一致的自检问题Relay 提供的、用于向服务端发起订阅的函数名叫什么答案requestSubscription赞分享【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载相关推荐howtographql React Relay 教程用 Relay 指令式 API 实现投票 Mutation 与 Store 缓存更新howtographql React Relay 教程用 Relay 指令式 API 实现投票 Mutation 与 Store 缓存更新 本文基于 HoHowToGraphQL React Apollo 实战用 GraphQL Subscriptions 与 WebSocketLink 实现实时数据推送HowToGraphQL React Apollo 实战用 GraphQL Subscriptions 与 WebSocketLink 实现实时数据推送howtographql 中 Angular Apollo 实战用 GraphQL Subscriptions 实现 Hacker News 克隆的实时更新howtographql 中 Angular Apollo 实战用 GraphQL Subscriptions 实现 Hacker News 克隆的实时更上一篇HefeiAicc/vicuna-7b-1.1开源对话模型终极指南从入门到精通下一篇开发者必读gpt-oss-20b-tq3 API使用教程与常见问题解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
XGo 的 for 语句完全指南:从 for..in 迭代语法到 XGo_Enum 自定义迭代协议 编程语言编译器开发工具 【免费下载链接】xgo XGo is a programming language that reads like plain English. But its also incredibly powerful — it lets you leverage assets from C/C, Go, Python, and JavaScript/TypeScript, creating a unified software engineering… · 2026/9/25 3:05:00
Learn-Algorithms 排序算法全景解析:稳定性、复杂度与七大经典排序的源码级实战 教程 【免费下载链接】Learn-Algorithms 算法学习笔记 项目地址: https://gitcode.com/gh_mirrors/le/Learn-Algorithms 点击查看 免费下载 本文以 6 Sort/README.md 为骨架,系统梳理排序稳定性的定义、比较排序与线性排序两大阵营的复杂度边界… · 2026/9/25 3:05:00
GHelper 华硕笔记本控温:压低风扇噪音、保护电池的实用指南 GHelper 华硕笔记本控温:压低风扇噪音、保护电池的实用指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenboo… · 2026/9/25 3:05:00
国密nginx搭建全攻略:从GmSSL编译到SM2双证书部署 最近几年做政企项目、金融类系统的朋友,基本都会碰到同一个需求:客户要求网站全链路支持国密算法,浏览器访问不再走传统的RSA体系,而是用SM2做密钥协商、SM3做摘要、SM4做数据加密。这时候你的第一反应大概率是“把nginx的ssl证书… · 2026/9/25 3:32:15
轻量级密码PRESENT与改进版WidePresent:从低功耗硬件到扩散增强 只要聊到轻量级分组密码算法,PRESENT就是那道绕不开的坎。我在一个低功耗物联网表计项目里做安全方案时,对这个结论体会尤其深:主控MCU频率不到200MHz,Flash只有几十KB,每一帧上行的加密都要精确到毫安级功耗。团队里有… · 2026/9/25 3:32:15
银河麒麟与Windows双系统启动顺序深度解析 1. 项目概述:为什么改启动顺序不是“点几下鼠标”的事你装好了银河麒麟V10和Windows 11双系统,开机却总先进入Windows——不是你按错了键,是GRUB菜单压根没弹出来;或者GRUB倒是出来了,但麒麟排在第三行,Win… · 2026/9/25 3:32:09
NAT10下游基因预测:生物信息学与机器学习全流程解析 简介:一个面向生物信息学与机器学习交叉应用的NAT10下游基因预测项目资源包,适合生信初学者、研究生及关注基因调控机制的研究者参考。资源围绕与NAT10相关的GEO表达数据集展开,完整覆盖数据提取、清洗、标准化,以及基于支持向量机… · 2026/9/25 3:32:09
WinForm与DevExpress控件继承体系解析 1. WinForm与DevExpress控件继承体系解析在Windows Forms应用程序开发中,DevExpress控件套件因其丰富的UI组件和强大的功能而广受欢迎。但许多开发者在从原生WinForm控件转向DevExpress控件时,经常会遇到一个看似简单却令人困惑的问题:为什么… · 2026/9/25 3:32:09
AI编码代理失控怎么破?用Trellis给代理装上行为辅助轮 说实话,用AI编码代理写代码这件事,最让我崩溃的不是它"不会",而是它"太会了"。让它改个接口,它能顺手把整个模块的注释风格全改了;让它加一行日志,它能自作主张重构一个看似无关的函数… · 2026/9/25 3:32:09
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37