Next 14 SSR组件用Apollo预取数据后,突变时如何刷新客户端数据
Next.js + Apollo SSR 场景下评论计数更新方案
核心结论
SSR仅提供首屏渲染的初始数据,但组件后续的动态更新必须依赖客户端Apollo缓存——关键是把SSR预取的数据同步到客户端缓存,而非让组件直接依赖传入的props,这样就能兼顾SSR性能和客户端局部响应性。
具体实现步骤
1. SSR预取时同步数据到客户端缓存
在Next.js的getServerSideProps(或getStaticProps+ISR)中,完成查询后将结果写入Apollo缓存,再把缓存序列化后传递给客户端:
// 页面组件的getServerSideProps export async function getServerSideProps(context) { // 初始化服务器端Apollo Client const apolloClient = initializeApollo(); // 预取评论计数 const { data } = await apolloClient.query({ query: GetCommentCount }); // 将查询结果写入服务器端缓存 apolloClient.cache.writeQuery({ query: GetCommentCount, data, }); return { props: { // 序列化缓存,传给客户端 initialApolloState: apolloClient.cache.extract(), }, }; }
客户端初始化Apollo Client时,恢复这个序列化的缓存:
let apolloClient; export function initializeApollo(initialState = null) { const _apolloClient = apolloClient ?? new ApolloClient({ uri: '/api/graphql', cache: new InMemoryCache().restore(initialState), }); // 合并初始缓存(如果有) if (initialState) { const existingCache = _apolloClient.cache.extract(); _apolloClient.cache.restore({ ...existingCache, ...initialState }); } // 服务端渲染时每次返回新实例,客户端复用单例 if (typeof window === 'undefined') return _apolloClient; if (!apolloClient) apolloClient = _apolloClient; return _apolloClient; }
2. 组件通过useQuery读取缓存
CommentCount组件不再直接使用传入的初始值,而是通过useQuery订阅Apollo缓存,这样缓存更新时组件会自动重渲染:
import { useQuery } from '@apollo/client'; import { GetCommentCount } from '../graphql/queries'; function CommentCount() { const { data } = useQuery(GetCommentCount); return <div>评论数:{data?.commentCount}</div>; } export default CommentCount;
3. 突变时保持乐观更新逻辑
触发AddComment突变时,和纯客户端模式一样使用乐观更新+缓存更新,因为客户端已经有完整的缓存数据,操作后组件会即时响应:
import { useMutation } from '@apollo/client'; import { AddComment } from '../graphql/mutations'; import { GetCommentCount } from '../graphql/queries'; function AddCommentForm() { const [addComment] = useMutation(AddComment, { // 乐观响应,即时更新UI optimisticResponse: { __typename: 'Mutation', addComment: { __typename: 'Comment', id: 'temp-comment-id', content: input.content, }, }, // 更新评论计数缓存 update(cache, { data: { addComment } }) { const { commentCount } = cache.readQuery({ query: GetCommentCount }); cache.writeQuery({ query: GetCommentCount, data: { commentCount: commentCount + 1 }, }); }, }); // 表单提交逻辑... }
针对你的疑问的明确解答
- 触发突变后如何确保组件更新? 只要客户端缓存已同步SSR预取的数据,组件通过
useQuery订阅缓存,突变时更新缓存后,组件会自动重新渲染,无需刷新页面。 - SSR仅提供初始值,组件仍需从客户端缓存更新? 是的,SSR的核心作用是渲染首屏静态内容,后续的动态交互必须依赖客户端缓存,这样才能实现局部更新。
- 是否需要客户端重新请求填充缓存? 不需要。通过SSR预取时序列化缓存并传给客户端初始化,客户端缓存会直接包含首屏数据,无需二次请求后端。
标准模式总结
这是Next.js + Apollo官方推荐的标准模式:
- SSR预取数据并写入缓存,序列化后传递给客户端;
- 客户端初始化Apollo时恢复缓存;
- 组件通过
useQuery读取缓存,自动订阅变化; - 突变时通过乐观更新+缓存修改实现即时UI响应。
这种模式既利用了SSR的首屏性能优势,又保留了客户端异步交互的局部响应性,完全避免了重复请求。
内容的提问来源于stack exchange,提问作者Stewart Alan
相关产品推荐
相关产品推荐

