Next 13服务端组件与Apollo Client缓存控制问题咨询
一、路由导航时控制RSC重新请求时机
Next 13中使用Link组件做客户端路由时,默认会复用服务端渲染组件(RSC)的缓存以减少重复请求。要按需触发重新请求,可采用以下方式:
手动触发页面刷新:在客户端组件中,通过
next/router的router.refresh()方法强制刷新当前页面的RSC数据。比如在导航完成后调用:'use client'; import { useRouter } from 'next/router'; import Link from 'next/link'; const NavLink = ({ href, children }) => { const router = useRouter(); return ( <Link href={href} onClick={() => setTimeout(() => router.refresh(), 0)} > {children} </Link> ); };这种方式会让目标页面的所有RSC重新从服务端获取数据。
自定义RSC缓存键:使用Next的
unstable_cache(注意:该API为实验性,生产环境需谨慎)封装数据请求,通过动态缓存键控制失效时机。比如加入用户状态或时间戳参数,参数变化时自动触发新请求:import { unstable_cache } from 'next/cache'; const fetchData = unstable_cache( async (userId) => { // 数据请求逻辑,比如GraphQL查询 const res = await fetch('/api/data', { method: 'POST', body: JSON.stringify({ userId }) }); return res.json(); }, ['user-data'], // 基础缓存键 { tags: [`user:${userId}`] } // 标签用于后续主动失效 ); // 在RSC中调用 export default async function UserProfile({ userId }) { const data = await fetchData(userId); return <div>{data.name}</div>; }后续若需要主动失效,可调用
revalidateTag('user:123')。拆分高频刷新部分为客户端组件:如果某部分数据需要频繁刷新,可将其拆分为客户端组件,使用SWR/React Query这类客户端数据管理库,通过
refreshInterval或手动触发刷新控制,同时保留其他部分的RSC优势。
二、RSC结合Apollo Client的数据刷新与缓存失效
针对硬刷新时Apollo Client返回的serverDate不更新的问题,以及定时刷新需求,可从以下几点调整:
1. 避免服务端Apollo Client单例复用
在RSC中创建Apollo Client实例时,不要使用全局单例,否则服务端的缓存会在多个请求之间共享,导致旧数据被复用。每次请求都应创建新的Client实例:
// lib/apollo-server.ts import { ApolloClient, InMemoryCache } from '@apollo/client'; export function createApolloClient() { return new ApolloClient({ uri: '/api/graphql', cache: new InMemoryCache(), // 禁用服务端缓存,确保每次请求拉取最新数据 defaultOptions: { query: { fetchPolicy: 'network-only', }, }, }); } // 在RSC中使用 import { createApolloClient } from '../lib/apollo-server'; export default async function ServerComponent() { const client = createApolloClient(); const { data } = await client.query({ query: YOUR_QUERY }); return <div>Server Date: {data.serverDate}</div>; }
2. 配置页面动态渲染策略
确保页面组件设置正确的动态渲染配置,禁用静态缓存:
// 页面组件顶部 export const dynamic = 'force-dynamic'; // 或者设置revalidate时间,指定x秒后重新验证 export const revalidate = 60; // 60秒后重新请求数据
revalidate会让Next在指定时间后自动重新渲染RSC,同步更新数据。
3. 同步服务端与客户端缓存失效
要让客户端缓存与服务端RSC的数据同步,可在RSC中传递最新的serverDate或数据版本号给客户端组件,客户端组件收到后主动更新Apollo缓存:
// RSC组件 export default async function ServerComponent() { const client = createApolloClient(); const { data } = await client.query({ query: GET_SERVER_DATE }); return <ClientComponent serverDate={data.serverDate} />; } // 客户端组件 'use client'; import { useApolloClient } from '@apollo/client'; export default function ClientComponent({ serverDate }) { const client = useApolloClient(); // 当服务端传递的serverDate更新时,更新客户端缓存 useEffect(() => { client.writeQuery({ query: GET_SERVER_DATE, data: { serverDate }, }); }, [client, serverDate]); // 客户端使用缓存数据 const { data } = useQuery(GET_SERVER_DATE); return <div>Client Date: {data.serverDate}</div>; }
4. 定时触发RSC重新验证
如果需要指定组件在x秒后自动重新请求,除了页面级的revalidate,还可以对单个RSC的请求使用unstable_cache配置过期时间:
const fetchServerDate = unstable_cache( async () => { const client = createApolloClient(); const { data } = await client.query({ query: GET_SERVER_DATE }); return data.serverDate; }, ['server-date'], { revalidate: 10 } // 10秒后自动重新请求 ); // 在RSC中调用 export default async function ServerDateComponent() { const serverDate = await fetchServerDate(); return <div>Server Date: {serverDate}</div>; }
内容的提问来源于stack exchange,提问作者Stewart Alan

