You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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官方推荐的标准模式:

  1. SSR预取数据并写入缓存,序列化后传递给客户端;
  2. 客户端初始化Apollo时恢复缓存;
  3. 组件通过useQuery读取缓存,自动订阅变化;
  4. 突变时通过乐观更新+缓存修改实现即时UI响应。

这种模式既利用了SSR的首屏性能优势,又保留了客户端异步交互的局部响应性,完全避免了重复请求。

内容的提问来源于stack exchange,提问作者Stewart Alan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:13:14