Relay 是 Facebook(现 Meta)为 GraphQL 应用打造的全栈式客户端方案,它的核心竞争力不只是一个请求库,而是一整套编译期到运行时的数据工程化体系:编译器在构建阶段解析查询、生成类型化 artifact,运行时通过 normalize 存储管理碎片化数据。这套体系原生绑定 React,官方只提供了 react-relay 包,Vue 3 项目想直接落地确实存在门槛,但并非无解。本文将从原理到实操,完整讲解在 Vue 3 中集成 Relay 的几种路径。

一、理解 Relay 的分层架构:为什么它不直接支持 Vue
Relay 的架构分为三层。第一层是 relay-compiler 编译器,它在构建阶段扫描代码中的 graphql() 调用,把 GraphQL 文本编译成独立的 JSON artifact 文件,这一层与任何 UI 框架无关,纯 JavaScript 工具链。第二层是 relay-runtime 运行时,包含 Environment、Store、Network 层以及 normalize、分页、订阅等核心能力,同样是框架无关的。真正绑定 React 的只有第三层 react-relay,它用 Hooks 和 Suspense 把运行时数据接到了组件生命周期上。
这意味着 Vue 3 要接入 Relay,前两层可以直接复用,需要重写的只是"绑定层"。社区里 vue-relay 这类库本质上就是用 Vue 的响应式系统和 Composition API 重新实现了 useFragment、useQuery、useMutation 这些 Hooks。理解了这一点,你就能判断自己需要的是完整的社区方案,还是基于 relay-runtime 自己封装一个轻量绑定。
二、工程化配置:让 Relay 编译器跑进 Vite 项目
第一步是安装基础依赖并初始化 Relay 配置。Vue 3 项目通常使用 Vite,推荐用 vite-plugin-relay 在开发阶段实时编译 GraphQL,同时保留 relay-compiler 的 watch 模式用于生成 artifact。
// 安装:npm i relay-runtime relay-compiler vite-plugin-relay -D
// relay.config.js
module.exports = {
src: './src',
schema: './schema.graphql',
language: 'javascript',
artifactDirectory: './src/__generated__',
eagerEsModules: true,
};
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import relay from 'vite-plugin-relay';
export default defineConfig({
plugins: [vue(), relay],
});配置完成后,在 package.json 中加入 relay 脚本命令 relay-compiler --watch,开发时与 Vite 并行运行。编译器会读取源码中的 graphql 标签,在 __generated__ 目录产出类型化的 artifact 文件,这些文件描述了每个 fragment 对应的数据形状,运行时依赖它做数据校验与读取。
需要注意 eagerEsModules 选项,开启后 artifact 会以 ESM 形式输出,避免 Vite 开发服务器中 CommonJS 互操作导致的读取失败。这是 Vue 3 加 Vite 场景下最常见的坑之一,如果遇到 artifact 导入为空对象,优先检查这一项。
三、封装 Vue 3 绑定层:useQuery 与 useFragment 的实现
有了编译产物,接下来封装 Hooks。核心思路是:查询用 fetchQuery 或 request 发起,把结果写入 Environment 的 Store;组件内用 readInlineData 或 Store 的订阅 API 读取 fragment,并借助 Vue 的 ref 建立响应式桥接。
// relay/useQuery.js
import { ref, onMounted } from 'vue';
import { fetchQuery, Environment, Network, RecordSource, Store } from 'relay-runtime';
export const environment = new Environment({
network: Network.create(async (params, variables) => {
const res = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: params.text,
variables,
}),
});
return res.json();
}),
store: new Store(new RecordSource()),
});
export function useQuery(preloadedQuery) {
const data = ref(null);
const error = ref(null);
onMounted(() => {
const request = fetchQuery(
environment,
preloadedQuery,
preloadedQuery.variables
);
request.subscribe({
next: (result) => { data.value = result; },
error: (err) => { error.value = err; },
});
});
return { data, error };
}fragment 的处理更体现 Relay 的特色。每个组件声明自己需要的数据碎片,父组件通过 props 传递 fragment 引用,子组件用 readInlineData 读取,这样组件之间形成清晰的数据边界,任何一个组件的字段变更只影响该组件重新编译,这对大型项目的迭代效率提升非常明显。
// components/UserProfile.vue 中的 fragment 读取
import { graphql, readInlineData } from 'relay-runtime';
import { computed } from 'vue';
const userFragment = graphql`
fragment UserProfile_user on User {
id
name
avatarUrl
}
`;
export function useUserProfile(userRef) {
return computed(() => readInlineData(userFragment, userRef.value));
}上述代码中 readInlineData 会在读取的同时做一次浅校验,如果数据缺失会返回 null,此时你可以在模板中做空态兜底。如果要获得完整的订阅式更新能力,可以改用 environment.getStore().lookup 配合 retain,手写一个订阅回调把变更同步进 ref,代码量比 React 版多一些,但逻辑完全可控。
四、与 Apollo Client、urql 的取舍对比
Vue 生态里 @apollo/client 配合 @vue/apollo-composition 是主流选择,开箱即用、文档齐全;urql 则胜在轻量与插件化。Relay 的优势在另一端:编译期校验能在 CI 阶段拦截不合规查询,colocated fragment 让百人团队的 schema 演进不再互相踩踏,服务端如果配合 Relay Server Specification(如 Node ID 规范、cursor 分页规范),缓存一致性还能再上一个台阶。
| 维度 | Relay | Apollo Client | urql |
|---|---|---|---|
| Vue 官方支持 | 无,需社区或自封装 | 有官方组合式 API | 有第三方绑定 |
| 编译期校验 | 强,编译器驱动 | 无 | 可选插件 |
| 包体积 | 中等偏大 | 较大 | 小 |
| 适合规模 | 大型多人协作 | 中小到大型均可 | 中小型 |
结论很直接:如果团队规模小、迭代节奏快、后端 schema 变动频繁但没有配合 Relay 规范,Apollo 或 urql 是更务实的选择;如果项目页面数量多、组件层级深、多团队共享数据图,并且服务端愿意遵循 Relay 规范,那么在 Vue 3 中投入成本封装 Relay 绑定层是值得的长期投资。自封装的 Hooks 层通常只需三五百行代码,维护成本远低于 Relay 编译期校验带来的收益。
落地建议按三步走:先在独立模块中跑通 Environment 与 fetchQuery,再实现 useQuery 与 useFragment 两个核心 Hooks 并补齐错误与加载态,最后接入 useMutation 与 useSubscription,通过 commitMutation 的 updater 配置实现乐观更新。每一步都保持与 React 版 API 命名一致,团队后续迁移或混合使用时认知成本最低。
Vue 3RelayGraphQL客户端修改时间:2026-09-03 11:33:28