Apollo Client是目前最广泛使用的GraphQL客户端库,支持React、Vue、Angular、Svelte等框架。它的核心功能是缓存、状态管理和网络请求。
安装和配置:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: 'https://api.example.com/graphql', }); const authLink = setContext((_, { headers }) => { const token = localStorage.getItem('token'); return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', } }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network' }, query: { fetchPolicy: 'network-only' }, } });
在React中使用:
import { useQuery, useMutation, gql } from '@apollo/client'; const GET_USER = gql` query GetUser($id: ID!) { user(id: $id) { id name email posts(limit: 5) { id title likesCount } } } `; function UserProfile({ userId }: { userId: string }) { const { data, loading, error, refetch } = useQuery(GET_USER, { variables: { id: userId }, // 轮询 pollInterval: 30000, // 错误处理 onError: (err) => console.error('查询失败:', err), }); if (loading) return <div>加载中...</div>; if (error) return <div>出错了: {error.message}</div>; return ( <div> <h2>{data.user.name}</h2> <p>{data.user.email}</p> <button onClick={() => refetch()}>刷新</button> <h3>最近帖子</h3> {data.user.posts.map(post => ( <div key={post.id}>{post.title} ({post.likesCount}赞)</div> ))} </div> ); }
Mutation使用:
const LIKE_POST = gql` mutation LikePost($postId: ID!) { toggleLike(postId: $postId) { post { id likesCount isLiked } } } `; function PostCard({ post }) { const [likePost] = useMutation(LIKE_POST, { // 乐观更新:不等服务端响应就更新UI optimisticResponse: { toggleLike: { post: { id: post.id, likesCount: post.isLiked ? post.likesCount - 1 : post.likesCount + 1, isLiked: !post.isLiked, } } }, // 服务端响应后更新缓存 update: (cache, { data }) => { cache.modify({ id: cache.identify(data.toggleLike.post), fields: { likesCount: () => data.toggleLike.post.likesCount, isLiked: () => data.toggleLike.post.isLiked, } }); } }); return ( <div> <h4>{post.title}</h4> <button onClick={() => likePost({ variables: { postId: post.id } })}> {post.isLiked ? '取消赞' : '赞'} ({post.likesCount}) </button> </div> ); }
Apollo Client的缓存策略通过 fetchPolicy 控制:
cache-first:优先用缓存,缓存没有才请求(默认)cache-and-network:先返回缓存数据,同时发网络请求更新network-only:每次都请求网络cache-only:只用缓存,不发请求no-cache:每次请求网络,不缓存结果Relay是Facebook开发的GraphQL客户端,专为大型应用设计。它的设计哲学是"编译时优化"——查询在构建时通过Relay Compiler编译成高度优化的运行时代码。
Relay的核心概念:
Fragment容器:每个React组件声明它需要的数据片段(Fragment),Relay在构建时收集所有组件的Fragment,组合成高效的查询。
// UserAvatar 组件声明自己需要 name 和 avatarUrl const UserAvatar = createFragmentContainer( ({ user }) => ( <div> <img src={user.avatarUrl} alt={user.name} /> <span>{user.name}</span> </div> ), { user: graphql` fragment UserAvatar_user on User { name avatarUrl } ` } ); // PostList 组件嵌套 UserAvatar,声明自己需要帖子数据 const PostList = createFragmentContainer( ({ posts }) => ( <div> {posts.map(post => ( <div key={post.id}> <h4>{post.title}</h4> <UserAvatar user={post.author} /> </div> ))} </div> ), { posts: graphql` fragment PostList_posts on Post @relay(plural: true) { id title author { ...UserAvatar_user } } ` } );
Relay Compiler在构建时分析这些Fragment的引用关系,生成组合查询——每个页面只发一个查询请求,所有组件的数据需求都合并进去。
@relay指令:Relay使用自定义指令标记类型关系。@relay(plural: true) 表示这是一个列表类型。@connection 标记分页连接。
| 维度 | Apollo Client | Relay |
|---|---|---|
| 学习曲线 | 中等 | 陡峭 |
| 编译步骤 | 不需要(运行时处理) | 需要(Relay Compiler) |
| 缓存机制 | InMemoryCache(规范化) | Store(规范化) |
| 数据获取 | useQuery hook | useLazyLoadQuery + usePreloadedQuery |
| 分页 | 手动处理 | @connection自动处理 |
| 性能优化 | 运行时优化 | 编译时优化 |
| 生态支持 | 全框架 | React为主 |
| 适合场景 | 中小型项目 | Facebook级别大型应用 |
对于大多数项目,Apollo Client是更好的选择——它的API更直观、生态更丰富、文档更完善。Relay在性能极致追求的场景下有价值,但学习成本和工具链复杂度是显著的。
Apollo Client 的查询在运行时解析和规范化,好处是改查询立即生效、不需要构建步骤,坏处是每次查询都要在运行时做文档解析和缓存归一化。Relay 则把查询编译成高度优化的指令码,运行时只执行编译产物,查询与组件的对应关系在构建期就固定下来。
这个差异带来的实际影响:Apollo 项目改一个查询字段,热更新就能看到效果;Relay 项目必须重新跑编译器,但生产包更小、运行时开销更低。Relay 还要求所有数据访问都走 Fragment 和 connection 模式,约束强、规范统一,适合大型团队。
// Relay 的查询入口是预加载式:先取数据再渲染组件 const preloadedQuery = loadQuery(environment, UserProfileQuery, { userId }); return <UserProfile queryRef={preloadedQuery} />;
选型的现实建议:团队规模小、迭代快、需要灵活改查询,选 Apollo;团队规模大、页面数据依赖复杂、愿意为性能接受工具链约束,考虑 Relay。