Relay
Meta 出品的 GraphQL 客户端框架,通过编译时优化和声明式数据获取,为 React 应用提供可扩展的数据驱动架构。
Relay 是由 Meta(Facebook)开发并维护的 GraphQL 框架,通过 Rust 编译器在构建阶段完成查询验证和优化,实现极致的性能和类型安全。核心设计为"片段驱动"和"数据遮蔽",v21 引入一等公民 TypeScript 支持和实验性 RSC 支持。GitHub ~18,900 Stars,在 Meta 内部驱动数万个组件。
Relay
Meta 出品的 GraphQL 客户端框架,通过编译时优化和声明式数据获取,为 React 应用提供可扩展的数据驱动架构。
技术简介说明
Relay 是由 Meta(Facebook)开发并维护的 JavaScript 框架,专为构建数据驱动的 React 应用而设计。自 2016 年开源以来,Relay 一直是 GraphQL 生态中最具影响力的客户端方案之一。与 Apollo Client 等灵活的通用 GraphQL 客户端不同,Relay 采用了高度定制化的架构——通过 Rust 编写的编译器在构建阶段完成查询验证、优化和代码生成,将大量工作从运行时转移到编译时,从而实现极致的性能和类型安全。
Relay 的核心设计哲学是**"片段驱动"(Fragment-Centric)和"数据协作定位"(Colocation):每个组件通过 GraphQL 片段(Fragment)声明自身的数据需求,Relay 自动将这些片段组合、去重并批量发送请求。这种模式消除了"上帝查询"(God Query)问题,使组件真正解耦。配合数据遮蔽**(Data Masking)机制,组件只能访问其显式声明的字段,防止隐式依赖和数据耦合。
截至 2026 年中,Relay 在 GitHub 上拥有约 18,900 Stars,最新版本为 v21.0.1(2026 年 5 月 27 日发布)。v21 是里程碑版本,引入了一等公民的 TypeScript 支持(内置 .d.ts 类型定义,无需 @types/relay-runtime)、实验性 React Server Components 支持(react-relay/rsc_EXPERIMENTAL 入口)、增强的 @catch 错误处理(支持 @connection 字段和客户端边缘字段)、Relay Resolvers 新语法(废弃旧版 docblock 语法,使用 @relayField / @relayType)、@oneOf 输入支持(生成更严格的判别联合类型)、以及移除 @live_query 指令(迁移至 @client_polling 和 @live)。编译器性能方面,v21 实现了客户端扩展转换速度提升 89%(通过 PointerAddress 缓存)、并行化 Schema 解析和变更检测、以及 FlatBuffer 二进制 Schema 序列化。Relay 在 Meta 内部被广泛用于 Facebook.com、Instagram、Oculus 等产品,驱动着数万个界面组件,是 Meta 移动和 Web API 架构的"北极星"。
基本信息
| 项目 | 详情 |
|---|---|
| 官网 | https://relay.dev/ |
| GitHub | https://github.com/facebook/relay |
| License | MIT |
| 最新版本 | [email protected](2026 年 5 月 27 日) |
| 主要语言 | Rust(52.4%)、JavaScript(31.4%)、MDX(14.7%)、TypeScript(0.3%) |
| 维护者 | Meta Platforms, Inc. |
| 核心维护人员 | Joe Savona (@josephsavona)、Jordan Eldredge (@captivation)、Robert Balicki (@robertbalicki) |
| GitHub Stars | ~18,900 ⭐ |
| Forks | ~1,900 |
| 累计发布版本 | 92 |
| 总提交数 | 11,476 |
| 包管理器 | npm / pnpm / yarn |
| Node.js 要求 | Node.js 16+ |
快速上手
安装
# npm
npm install react-relay relay-runtime graphql
npm install --save-dev relay-compiler babel-plugin-relay
# pnpm
pnpm add react-relay relay-runtime graphql
pnpm add -D relay-compiler babel-plugin-relay
# yarn
yarn add react-relay relay-runtime graphql
yarn add --dev relay-compiler babel-plugin-relay说明:
react-relay:React 绑定层,提供 Hooks 和 Providerrelay-runtime:独立的运行时核心(不依赖 React)relay-compiler:Rust 编写的编译器,负责验证、优化和生成代码babel-plugin-relay:Babel 插件,处理graphql标签graphql:GraphQL 参考实现(peer dependency)
基础配置
relay.config.js(项目根目录):
// relay.config.js
module.exports = {
// GraphQL Schema 文件路径
schema: './schema.graphql',
// 源代码目录(编译器扫描 graphql 标签)
src: './src',
// 生成语言:'javascript' | 'typescript' | 'flow'
language: 'typescript',
// 输出目录(生成的产物放在这里)
artifactDirectory: './src/__generated__',
// 持久化查询(可选,用于生产环境安全和性能优化)
persistConfig: {
url: 'https://api.example.com/graphql',
},
// ESM 输出(可选)
eagerESModules: true,
}Babel 配置(.babelrc 或 babel.config.js):
{
"plugins": ["relay"]
}如果使用
import graphql from 'babel-plugin-relay/macro'宏模式,可以跳过 Babel 插件配置。
package.json scripts:
{
"scripts": {
"relay": "relay-compiler",
"relay:watch": "relay-compiler --watch"
}
}最小示例
1. 创建 Relay Environment
// src/RelayEnvironment.ts
import { Environment, Network, RecordSource, Store } from 'relay-runtime'
// 网络层:定义如何发送 GraphQL 请求
async function fetchGraphQL(params: any, variables: Record<string, any>) {
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: params.text, // GraphQL 查询文本
variables,
}),
})
return response.json()
}
// 导出 Relay Environment 实例
export default new Environment({
network: Network.create(fetchGraphQL),
store: new Store(new RecordSource()),
})2. 定义查询并使用
// src/App.tsx
import React, { Suspense } from 'react'
import graphql from 'babel-plugin-relay/macro'
import {
RelayEnvironmentProvider,
useLazyLoadQuery,
} from 'react-relay'
import Environment from './RelayEnvironment'
// 定义 GraphQL 查询
const UserListQuery = graphql`
query AppUserListQuery {
users {
id
name
email
}
}
`
// 数据展示组件
function UserList() {
const data = useLazyLoadQuery(UserListQuery, {})
return (
<ul>
{data.users.map((user) => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
)
}
// 应用入口
export default function App() {
return (
<RelayEnvironmentProvider environment={Environment}>
<Suspense fallback={<p>加载中...</p>}>
<UserList />
</Suspense>
</RelayEnvironmentProvider>
)
}3. 运行编译器并启动
# 生成 GraphQL 产物(类型、查询 artifacts)
npm run relay
# 启动开发服务器
npm run dev核心概念与架构
架构总览
┌──────────────────────────────────────────────────────────────┐
│ Relay 框架 │
├──────────────┬───────────────┬───────────────────────────────┤
│ React/Relay │ Relay Runtime │ Relay Compiler (Rust) │
│ (react-relay│ (relay-runtime)│ • 查询验证 │
│ Hooks & │ • Store │ • 优化 & Transform │
│ Provider) │ • Network │ • TypeScript/Flow 生成 │
│ │ • RecordSource│ • 产物持久化 │
├──────────────┴───────────────┴───────────────────────────────┤
│ GraphQL Schema (.graphql) │
└──────────────────────────────────────────────────────────────┘
Relay 由三个核心库组成:
- Relay Compiler(Rust):在构建阶段扫描源码中的
graphql标签,验证查询与 Schema 的一致性,执行优化 Transform,生成 TypeScript/Flow 类型和运行时产物(artifacts)。编译器的 Rust 重写(自 v13 起)使编译速度提升了数十倍。 - Relay Runtime(relay-runtime):框架无关的核心层,提供 Store(规范化缓存)、Network(网络层抽象)、RecordSource(数据源)等基础设施。不依赖 React,可在任何 JS 环境使用。
- React/Relay(react-relay):将 Runtime 与 React 桥接的绑定层,提供
useLazyLoadQuery、useFragment、usePaginationFragment、useMutation等 Hooks。
Store(规范化缓存)
Relay 的 Store 是一个规范化缓存,将 GraphQL 响应按实体 ID 拆解存储。每个实体是一个 Record,通过全局唯一 ID(id 字段)索引。当 Mutation 返回的数据与已有实体 ID 匹配时,Store 自动更新所有引用该实体的组件——无需手动刷新。
import { Environment, Network, RecordSource, Store } from 'relay-runtime'
const store = new Store(
new RecordSource(),
{
// 可选:自定义 GC 策略
gcReleaseBufferSize: 10,
// 可选:查询缓存大小
queryCacheExpirationTime: 5 * 60 * 1000, // 5 分钟
}
)Fragment(片段)与数据遮蔽
Fragment 是 Relay 的核心抽象——每个组件声明自己需要的字段,而非整个页面的查询:
import graphql from 'babel-plugin-relay/macro'
import { useFragment } from 'react-relay'
// 子组件只声明自己需要的字段
const UserCardFragment = graphql`
fragment UserCard_user on User {
name
avatar
email
}
`
function UserCard({ user }) {
// useFragment 从 Store 中读取片段数据
const data = useFragment(UserCardFragment, user)
return <div>{data.name} - {data.email}</div>
}数据遮蔽意味着 UserCard 组件只能访问其 Fragment 中声明的字段,即使父查询返回了更多数据。这防止了组件间的隐式数据依赖。
Connection(连接)与分页
Relay 严格遵循 GraphQL Cursor Connection 规范,通过 @connection 和 @refetchable 指令实现分页:
fragment UserList_users on Query {
users(first: 10) @connection(key: "UserList_users") @refetchable(queryName: "UserListPaginationQuery") {
edges {
node {
id
...UserCard_user
}
}
pageInfo {
hasNextPage
endCursor
}
}
}import { usePaginationFragment } from 'react-relay'
function UserList({ query }) {
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment(
UserListPaginationQuery,
query
)
return (
<>
{data.users.edges.map(({ node }) => (
<UserCard key={node.id} user={node} />
))}
{hasNext && (
<button onClick={() => loadNext(10)} disabled={isLoadingNext}>
{isLoadingNext ? '加载中...' : '加载更多'}
</button>
)}
</>
)
}核心特性
1. 编译时验证与优化
Relay Compiler(Rust 编写)在构建阶段验证所有 GraphQL 查询与 Schema 的一致性。如果查询引用了不存在的字段或类型不匹配,编译直接失败——将运行时错误提前到构建时。编译器还执行多种优化 Transform,如字段去重、查询扁平化和死代码消除。
2. 一等公民的 TypeScript 支持
自 v21.0.0 起,Relay 编译器直接生成 TypeScript 类型定义,无需借助 GraphQL Code Generator 等第三方工具。Fragment 数据类型、分页查询类型、Mutation 类型均自动生成,与组件 Props 紧密绑定。
3. 数据遮蔽(Data Masking)
Relay 通过编译时强制实现数据遮蔽:组件只能通过 useFragment 访问其 Fragment 中显式声明的字段。即使父查询返回了额外字段,子组件也无法访问。这确保了组件的真正解耦和可维护性。
4. 自动批处理与请求去重
Relay 自动将多个 Fragment 合并为单个请求发送,消除了冗余字段和重复请求。当多个组件声明了重叠的字段时,编译器在构建阶段就去重,运行时只发送一个优化后的查询。
5. 乐观更新(Optimistic Updates)
Mutation 支持乐观响应,在服务器返回前立即更新 UI:
import { useMutation } from 'react-relay'
import graphql from 'babel-plugin-relay/macro'
const LikeMutation = graphql`
mutation LikePostMutation($input: LikeInput!) {
likePost(input: $input) {
post {
id
likeCount
}
}
}
`
function LikeButton({ postId }) {
const [commit] = useMutation(LikeMutation)
const handleLike = () => commit({
variables: { input: { postId } },
// 乐观响应:立即更新 UI
optimisticResponse: {
likePost: {
post: {
id: postId,
likeCount: currentCount + 1,
},
},
},
// 自定义 updater:手动更新 Store
updater: (store) => {
const post = store.get(postId)
post?.setValue((post.getValue('likeCount') as number) + 1, 'likeCount')
},
})
return <button onClick={handleLike}>点赞</button>
}6. 声明式分页
通过 usePaginationFragment Hook 和 @connection / @refetchable 指令,Relay 内置了分页逻辑。无需手动管理游标、拼接数据——loadNext() / loadPrevious() 一行搞定。
7. Suspense 优先的数据获取
Relay 深度集成 React Suspense,支持预加载查询(preloadQuery):在用户导航前就开始数据获取,实现零延迟的页面切换。
import { preloadQuery, usePreloadedQuery } from 'react-relay'
// 在路由切换时预加载
const preloaded = preloadQuery(Environment, ProfileQuery, { userId: '123' })
// 组件中消费预加载数据
function Profile({ preloadedQuery }) {
const data = usePreloadedQuery(ProfileQuery, preloadedQuery)
return <h1>{data.user.name}</h1>
}8. 持久化查询(Persisted Queries)
编译器支持将查询转换为 MD5 哈希 ID,生产环境只发送 ID 而非完整查询文本。服务端通过白名单验证,既减小 payload 体积,又防止未授权查询。
9. 流式渲染与增量更新
配合 React 18+ 的 Streaming SSR,Relay 支持在服务器渲染期间逐步发送数据,客户端通过 Suspense 边界渐进式渲染,无需等待所有数据就绪。
10. 框架无关的 Runtime
relay-runtime 不依赖 React,可以在 Vue、Svelte 或 Node.js 环境中独立使用。虽然 React 绑定是最成熟的,但核心缓存和网络层是通用的。
生态图
Relay 生态
├── 核心包
│ ├── react-relay # React 绑定(Hooks、Provider)
│ ├── relay-runtime # 框架无关的运行时核心
│ └── relay-compiler # Rust 编译器
│
├── 工具链
│ ├── babel-plugin-relay # Babel 插件(处理 graphql 标签)
│ ├── relay-config # 统一配置管理
│ └── vscode-relay # VS Code 扩展(语法高亮、跳转、诊断)
│
├── 服务端辅助
│ ├── graphql-relay # Relay 兼容服务端工具(Node ID、Connection 辅助)
│ └── express-graphql / Hono # GraphQL 服务端(需支持 Relay 规范)
│
├── 社区集成
│ ├── relay-hooks # 额外 React Hooks(离线支持等)
│ ├── found-relay # 路由集成(Found 路由 + Relay)
│ ├── next-relay # Next.js 集成方案
│ └── relay-test-utils # 测试工具(Mock Environment)
│
├── 开发工具
│ ├── Relay DevTools # 浏览器扩展(Store 检查、查询追踪)
│ └── relay-examples # 官方示例仓库
│
└── 第三方增强
├── WunderGraph # Relay RPC 集成(已迁移到 Cosmo)
├── Pothos + Relay # 类型安全的 GraphQL Schema 构建
└── graphql-codegen # 补充类型生成(与 Relay 内置生成互补)
适用场景
1. 大规模数据密集型应用
Relay 最擅长的是拥有数百个组件、复杂数据关系的大型应用。Facebook.com、Instagram 等产品驱动着数万个 Relay 组件。如果你的应用有大量嵌套列表、实时数据更新和复杂的分页需求,Relay 的编译时优化和数据遮蔽能显著提升可维护性。
2. 自建 GraphQL Schema 的项目
Relay 假设你拥有或能控制 GraphQL Schema。它要求服务端实现 Relay Server Specification(全局唯一 ID、Cursor Connection 分页、Mutation Input/Payload 模式)。如果你有自建的 GraphQL 后端且能遵循这些规范,Relay 能提供最佳体验。
3. 需要严格类型安全的项目
Relay 编译器自动生成 TypeScript/Flow 类型,与组件 Fragment 精确绑定。从 Schema → Fragment → 组件 Props,类型链路完整且一致。适合对类型安全有高要求的企业级项目。
4. 性能敏感的应用
编译时优化使 Relay 运行时开销极小:查询去重、字段扁平化、死代码消除、持久化查询——这些都在构建阶段完成。对于需要极致加载性能的应用(如移动端弱网环境),Relay 的优化效果显著。
5. 团队需要数据隔离的场景
数据遮蔽机制确保组件间不会产生隐式数据依赖。在多人协作的大型团队中,这防止了"改了一个组件导致另一个组件出问题"的级联风险。
6. 渐进式数据加载与 Suspense 路由
Relay 的预加载查询 + Suspense 模型天然支持"点击导航链接时即开始加载数据"的模式。配合 Next.js 或自定义路由,可以实现零延迟的页面切换体验。
7. 需要乐观更新与离线体验的应用
Relay 的乐观更新、自定义 updater 和 Store 操作 API 适合需要即时 UI 反馈的场景(如社交应用的点赞、评论)。配合 relay-hooks 社区包,还可以实现离线数据支持。
开发与工程化
项目结构建议
my-app/
├── src/
│ ├── __generated__/ # Relay 编译器生成产物(需提交到 Git)
│ ├── components/
│ │ ├── UserCard.tsx # 包含 Fragment 的组件
│ │ └── UserList.tsx # 包含 Query/Connection 的组件
│ ├── mutations/
│ │ └── LikePost.ts # Mutation 定义
│ ├── RelayEnvironment.ts # Environment 配置
│ └── App.tsx
├── schema.graphql # GraphQL Schema 文件
├── relay.config.js # Relay 编译器配置
├── babel.config.js # Babel 配置
└── package.json
编译器工作流
# 一次性编译
npx relay-compiler
# 监听模式(开发时推荐)
npx relay-compiler --watch
# 验证模式(CI 中检查产物是否最新)
npx relay-compiler --validate重要:
__generated__/目录中的文件应提交到版本控制。它们确保 CI 和团队成员无需手动运行编译器。
TypeScript 集成
Relay v21 起,编译器直接生成 TypeScript 类型。无需额外配置 GraphQL Code Generator:
import graphql from 'babel-plugin-relay/macro'
import { useFragment } from 'react-relay'
// 编译器自动生成 UserCard_user$key 类型
const UserCardFragment = graphql`
fragment UserCard_user on User {
name
email
}
`
// Props 类型使用生成的 $key
interface UserCardProps {
user: UserCard_user$key
}
function UserCard({ user }: UserCardProps) {
const data = useFragment(UserCardFragment, user)
// data 的类型自动推断为 { name: string; email: string }
return <div>{data.name} - {data.email}</div>
}测试策略
Relay 提供 relay-test-utils 包用于测试:
import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils'
const environment = createMockEnvironment()
// 模拟查询响应
environment.mock.queueOperationResolver((operation) =>
MockPayloadGenerator.generate(operation, {
User() {
return {
id: '1',
name: '张三',
email: '[email protected]',
}
},
})
)注意:Relay 的测试设置相对复杂,需要配置 Mock Environment 和 Operation 解析器。社区反馈这是主要的学习痛点之一。
Next.js 集成
Relay 可与 Next.js 配合实现 SSR,但需要额外配置:
# 推荐使用社区方案
npm install next-relay关键配置点:
- 服务端需要预取数据并传递给客户端
__generated__/目录需在next.config.js中配置 transpile- 持久化查询可配合 SSR 提升首屏性能
性能与安全
性能优化
| 优化手段 | 说明 |
|---|---|
| 编译时去重 | 编译器在构建阶段合并重复字段,减少 payload |
| 持久化查询 | 生产环境只发送查询 ID(MD5 哈希),大幅减小请求体积 |
| 查询扁平化 | 嵌套 Fragment 被编译器扁平化为单层查询,减少运行时处理 |
| 规范化缓存 | Store 按 ID 存储实体,避免重复渲染——只有相关 Fragment 变化的组件才会更新 |
| 精确更新 | 数据变化只触发声明了对应字段的 Fragment 所在的组件,而非整个子树 |
| 预加载 | preloadQuery 在导航时即开始数据获取,消除瀑布流请求 |
| GC 策略 | Store 内置垃圾回收,自动清理未引用的缓存数据 |
安全考量
| 安全特性 | 说明 |
|---|---|
| 持久化查询白名单 | 服务端只接受预注册的查询 ID,防止任意查询注入 |
| 编译时校验 | 所有查询在构建阶段验证,不合法的查询无法到达运行时 |
| 数据遮蔽 | 组件只能访问显式声明的字段,防止越权数据访问 |
| 无 eval / 动态查询 | Relay 不使用 eval() 或运行时动态构建查询,所有查询均为静态声明 |
技术对比
Relay vs Apollo Client vs urql
| 特性 | Relay | Apollo Client | urql |
|---|---|---|---|
| 维护者 | Meta | Apollo Inc. | Formidable |
| 设计理念 | 编译时优化、严格规范 | 运行时灵活、开发者友好 | 模块化、轻量 |
| GitHub Stars | ~18,900 | ~19,800 | ~9,000 |
| npm 周下载量 | ~150K(react-relay) | ~1,400K | ~600K |
| 编译器 | Rust 编译器(构建时必须) | 可选(GraphQL Code Generator) | 可选(Code Generator) |
| TypeScript | 编译器内置生成(v21+) | 需配合外部工具 | 需配合外部工具 |
| 缓存策略 | 规范化缓存(ID-based,固定策略) | 规范化缓存(策略可配置) | 可插拔(默认 document cache) |
| 学习曲线 | 陡峭 | 平缓 | 中等 |
| 数据遮蔽 | ✅ 编译时强制 | ❌ 无 | ❌ 无 |
| Schema 要求 | 需遵循 Relay Server Spec | 任意 GraphQL Schema | 任意 GraphQL Schema |
| 包体积 | 小(编译时剥离) | 较大(运行时灵活) | 最小(模块化) |
| Suspense 支持 | ✅ 核心设计 | ✅ 支持(4.x) | ✅ 支持 |
| RSC 支持 | 实验性(v21+) | 官方集成包 | 社区方案 |
| DevTools | 基础 DevTools | 完善的 Apollo DevTools | 基础 DevTools |
| SSR | 支持(需配置) | 成熟(hydration helpers) | 支持(exchange 模式) |
| 本地状态 | 无内置方案(用 React state) | Reactive Variables | 无(用 React state) |
| 错误处理 | 较弱(需自定义边界) | 丰富(ErrorPolicy、边界) | 基础 |
| 分页 | 内置(Connection 规范) | 需配置 Field Policy | 需自行实现 |
| 适用规模 | 大型应用 | 小到大型应用 | 小到中型应用 |
选择建议
| 场景 | 推荐 |
|---|---|
| 快速原型、小型项目 | urql 或 Apollo Client |
| 团队新人多、需要低学习曲线 | Apollo Client |
| 大规模数据密集应用 | Relay |
| 拥有自建 GraphQL Schema | Relay |
| 需要严格类型安全和数据隔离 | Relay |
| 使用第三方 GraphQL API(Schema 不可控) | Apollo Client |
| 需要丰富本地状态管理 | Apollo Client |
| 极致性能和包体积优化 | Relay 或 urql |
最佳实践
1. Fragment 设计原则
// ✅ 推荐:每个组件声明自己需要的最小字段集
const AvatarFragment = graphql`
fragment Avatar_user on User {
name
avatarUrl
}
`
// ✅ 推荐:通过 Fragment 组合实现数据聚合
const UserProfileFragment = graphql`
fragment UserProfile_user on User {
...Avatar_user # 组合子组件的 Fragment
...UserStats_user
bio
joinDate
}
`
// ❌ 避免:在 Fragment 中请求不需要的字段
const UserFragment = graphql`
fragment User_data on User {
name
email
phone # 当前组件不需要
address # 当前组件不需要
}
`2. Mutation 更新 Store
// ✅ 推荐:使用 @raw_response_type 和 updater 精确更新
const CreatePostMutation = graphql`
mutation CreatePostMutation($input: CreatePostInput!) {
createPost(input: $input) {
postEdge {
node {
id
title
...PostCard_post
}
}
}
}
`
const [commit] = useMutation(CreatePostMutation)
commit({
variables: { input: { title: '新文章' } },
updater: (store, data) => {
// 获取列表的 Connection
const list = store.getRoot().getLinkedRecord('posts')
// 创建新的 Edge 并插入
const newEdge = ConnectionHandler.insertEdge(list, data.createPost.postEdge, 'last')
},
})3. 错误处理
import { RelayEnvironmentProvider } from 'react-relay'
import { ErrorBoundary } from 'react-error-boundary'
// ✅ 推荐:使用 Error Boundary 捕获 Relay 查询错误
function App() {
return (
<RelayEnvironmentProvider environment={environment}>
<ErrorBoundary fallback={<ErrorPage />}>
<Suspense fallback={<Loading />}>
<MainContent />
</Suspense>
</ErrorBoundary>
</RelayEnvironmentProvider>
)
}4. 避免 Fragment Drilling
// ❌ 避免:手动传递 fragment reference
function Page({ queryRef }) {
const data = useLazyLoadQuery(PageQuery, {})
return <Child user={data.user} /> // 逐层传递
}
// ✅ 推荐:使用 @relay(mask: false) 展平数据
const ProfileFragment = graphql`
fragment Profile_user on User {
...Settings_user @relay(mask: false) # 不遮蔽,数据直接可用
...Avatar_user
}
`5. 生成产物管理
# ✅ 推荐:将 __generated__/ 提交到 Git
git add src/__generated__/
# CI 中验证产物是最新的
npx relay-compiler --validate技术局限与边界
1. 服务端 Schema 要求严格
Relay 要求 GraphQL 服务端实现 Relay Server Specification:
- 所有类型必须有全局唯一的
id字段 - 列表分页必须遵循 Cursor Connection 规范(
edges、node、cursor、pageInfo) - Mutation 必须使用
Input/Payload模式
如果你的 Schema 无法遵循这些规范(如使用第三方 API),Relay 将难以应用。
2. 学习曲线陡峭
相比 Apollo Client 的 useQuery 一步到位,Relay 需要理解 Fragment、Connection、Environment、Store、updater 等概念。社区反馈"fragment drilling"(嵌套组件间传递 Fragment Reference)和测试 mock 设置是主要痛点。
3. 编译器是单点依赖
所有 GraphQL 操作必须经过 Relay Compiler 处理。如果编译器报错(Schema 不匹配、查询语法错误等),整个项目无法构建。这既是优势(提前发现错误),也是约束(灵活性降低)。
4. 本地状态管理缺失
Relay 专注于服务端数据管理,不提供内置的本地状态方案。表单状态、UI 状态等需要自行使用 React state、Zustand 等外部方案。
5. 错误处理能力较弱
Relay 对 GraphQL 错误的处理相对基础。默认情况下,如果响应中包含 errors 字段但 data 不为 null,Relay 不会抛出异常。需要开发者自行在 fetchGraphQL 中检查和处理错误。
6. React Server Components 支持仍在实验阶段
v21 引入了实验性 RSC 支持,但尚未稳定。当前 Relay 的 Environment 通过 React Context 传递,与 RSC 的服务端/客户端边界存在天然张力。Meta 正在积极探索解决方案(GraphQLConf 2026 主题演讲)。
7. 社区生态相对较小
相比 Apollo 的丰富生态(DevTools、Studio、Router、MCP Server),Relay 的社区工具较少。集成方案(如 Next.js、SSR)主要依赖社区维护。
学习资源
官方资源
| 资源 | 链接 |
|---|---|
| 官方文档 | https://relay.dev/docs |
| 官方示例仓库 | https://github.com/relayjs/relay-examples |
| Relay 博客 | https://relay.dev/blog |
| VS Code 扩展 | https://marketplace.visualstudio.com/items?itemName=meta.relay |
推荐教程
| 资源 | 说明 |
|---|---|
| How to GraphQL — Relay 教程 | 从零开始的完整入门教程 |
| Relay 官方 Step-by-Step Guide | 官方分步指南 |
| Revisiting GraphQL in 2025: Type-Safe Stack with Pothos and Relay | 2025 年现代 Relay 技术栈实践 |
| React-Relay 2 years later | Relay 长期使用经验总结 |
| A deep-dive into Relay | Hasura 出品的深度解析 |
视频资源
| 视频 | 说明 |
|---|---|
| The Big Ideas in Relay — Jordan Eldredge, Meta (2025.11) | Meta 工程师解读 Relay 核心设计理念 |
| React Server Components Vs. GraphQL — GraphQLConf 2026 | RSC 与 GraphQL/Relay 的关系探讨 |
| Rewriting Relay's GraphQL Compiler in Rust | Rust 编译器重写历程 |
| Re-introducing Relay — React Conf 2021 | 新版 Relay 架构发布 |
| Building the New facebook.com with React, GraphQL and Relay | Facebook.com 实战案例 |
| Mobile GraphQL at Meta in 2025 | Meta 播客:2025 年移动端 GraphQL 实践 |
技术文章
| 文章 | 说明 |
|---|---|
| Apollo vs Relay (2026) | 2026 年最新对比 |
| Best GraphQL Clients for React in 2026 | PkgPulse 2026 年客户端横评 |
| Relay Modern: Simpler, faster, more extensible | Meta 工程博客:Relay Modern 设计 |
| Introducing the new Relay compiler | 官方:新编译器发布公告 |
2026 年现状
版本演进
截至 2026 年 7 月,Relay 的最新稳定版本为 v21.0.1(2026 年 5 月 27 日发布)。v21 是一个里程碑版本,主要变化包括:
- 一等公民的 TypeScript 支持:编译器直接生成
.d.ts类型定义,内置于relay-runtime和react-relay,无需安装@types/relay-runtime。Node.js ESM 命名导入在.mjs文件中正常工作 - 实验性 React Server Components 支持:新增
react-relay/rsc_EXPERIMENTAL入口,提供serverFetchQuery、serverReadFragment、serverPreloadQuery和useQueryFromServer等 API,支持服务器端数据获取和客户端水合 - 增强的 @catch 错误处理:
@catch指令现在支持@connection字段和客户端边缘字段,允许在分页和客户端边缘场景中优雅处理错误。编译器验证@match字段冲突 - Relay Resolvers 新语法:废弃旧版 docblock 语法(
@onType、@onInterface等),使用@relayField和@relayType。提供relay-compiler codemod fix-all自动迁移命令 - @oneOf 输入支持:生成更严格的判别联合类型,每次只允许一个属性,在编译时捕获多字段错误
- 移除 @live_query:该指令已移除,迁移至
@client_polling(interval: ...)进行轮询或@live进行服务器推送更新 - 文件名前缀不再必需:Fragment、Query 和 Mutation 名称不再需要以文件名前缀(此限制原为 Meta 内部 Haste 模块系统设计)
- @alias 支持 Fragment 定义:
@alias指令现在可用于 Fragment 定义,不仅限于内联 Fragment - 现代化 Flow 类型生成:生成的类型使用现代 Flow 语法(需要 Flow >= 0.250.0),如
(expr as Type)替代(expr: Type)、ReadonlyArray替代$ReadOnlyArray、unknown替代mixed - 编译器性能优化:客户端扩展转换速度提升 89%(通过
PointerAddress缓存)、并行化 Schema 解析和变更检测、FlatBuffer 二进制 Schema 序列化、使用DashMap替代RwLock - 新编译器 CLI 工具:实验性
experimental-regenerate-sub-schema和experimental-compare-document-ir子命令 - Bug 修复:修复超过 1000 个字段类型的 Schema 打印时的栈溢出、改进
@raw_response_type准确性、多项监听模式稳定性修复
版本历史:
| 版本 | 发布日期 | 关键特性 |
|---|---|---|
| v21.0.1 | 2026-05-27 | 修复 TypeScript 定义缺失、新增 --noWatchman 标志 |
| v21.0.0 | 2026-05-18 | 第一方 TypeScript、RSC 实验支持、@catch 增强 |
| v20.1.1 | 2025-08-06 | 回滚验证命令更新 |
| v20.1.0 | 2025-07-28 | 改进 TypeScript 错误消息 |
| v20.0.0 | 2025-06-14 | 废弃弱类型、改进文档 |
| v19.0.0 | 2025-05-02 | 要求 @alias、支持 React 19 |
| v18.2.0 | 2024-11-21 | Codemods、实验性分页钩子 |
| v18.1.0 | 2024-10-04 | 稳定 Relay Resolvers |
| v18.0.0 | 2024-09-07 | 稳定 @alias、@catch 指令 |
| v17.0.0 | 2024-06-14 | 严格 Schema 验证 |
在 Meta 内部的使用
Relay 仍然是 Meta 的核心基础设施。根据 2026 年 2 月的 Meta Careers 播客,Relay 继续作为 Meta 移动 API 设计的"北极星"——移动 GraphQL API 的架构直接受 Relay 规范影响。Facebook.com、Instagram、Oculus 等产品仍在大规模使用 Relay,驱动着数万个组件。
RSC 与 GraphQL 的融合
2026 年 5 月的 GraphQLConf 上,Meta 工程师 Jordan Eldredge 发表了主题演讲"React Server Components Vs. GraphQL",探讨 RSC 与 GraphQL/Relay 之间的关系——两者在某种程度上是解决相同问题的不同方案(声明式数据获取、服务端渲染),但各有优势。Meta 正在探索将两者融合的路径。
社区与生态
- GitHub Stars:~18,900,保持活跃维护(92 个版本、11,476 次提交)
- VS Code 扩展:2025 年 4 月更新,提供语法高亮、跳转定义、实时诊断
- 社区集成:WunderGraph 的 Relay 集成已迁移到 Cosmo 平台;Pothos + Relay 组合成为 2025 年类型安全 GraphQL 栈的热门选择
- 下载量:
react-relay周下载量约 15 万次,相比 Apollo Client 的 140 万次仍有差距,但在大规模应用中保持稳定增长
未来方向
- RSC 正式支持:预计后续版本将把实验性 RSC 支持推向稳定
- 编译器持续优化:Rust 编译器持续改进编译速度和产物质量
- 开发者体验改善:社区持续呼吁简化 Fragment Drilling、改善错误信息和测试体验
- 与 Next.js App Router 的更好集成:随着 Next.js App Router 成为主流,Relay 的 SSR/RSC 集成方案是社区关注焦点
总结
Relay 是一个为大规模应用而生的 GraphQL 框架,而非简单的数据获取库。它的编译时优化、数据遮蔽和严格规范使其在超大规模应用中无可替代——Meta 内部数万个组件的成功实践就是最好的证明。
适合选择 Relay 的信号:
- 你拥有或能控制 GraphQL Schema
- 应用规模大、组件多、数据关系复杂
- 团队需要严格的数据隔离和类型安全
- 对性能有极致要求
不适合 Relay 的信号:
- 使用第三方 GraphQL API(Schema 不可控)
- 小型项目或快速原型
- 团队对 GraphQL 和 Relay 概念不熟悉,需要快速上手
- 需要灵活的本地状态管理方案
"Relay 是为那些愿意前期投入学习成本、以换取后期规模扩展无忧的团队准备的。"