Next.js中Apollo Client服务端调用客户端导出报错,求用户页查询修复方案
问题根源
这个错误是Next.js App Router的环境限制导致的:你的apollo.ts里的Apollo Client初始化代码属于客户端专属逻辑(比如依赖浏览器环境的API),但在服务器端渲染(SSR)过程中,user/[userId]/page.tsx或layouts.tsx这类默认的服务器组件尝试导入并调用了它,触发了跨环境调用的报错。
修复步骤
1. 修正apollo.ts的客户端标识
给文件添加'use client'指令,明确标记为客户端组件,同时对依赖浏览器的逻辑做环境判断:
'use client'; // 必须添加这行,强制该文件在客户端运行 import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: '/graphql', // 替换为你的GraphQL端点 }); const authLink = setContext((_, { headers }) => { // 仅在客户端环境读取localStorage const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); export default client;
2. 拆分页面的客户端逻辑
服务器组件不能直接使用Apollo的useQuery等Hook,需要把查询逻辑抽成独立的客户端组件:
比如创建user/[userId]/UserProfileClient.tsx:
'use client'; import { useQuery } from '@apollo/client'; import { GET_USER } from '../../queries/GET_USER'; interface UserProfileProps { userId: string; } export default function UserProfileClient({ userId }: UserProfileProps) { const { loading, error, data } = useQuery(GET_USER, { variables: { userId }, }); if (loading) return <p>加载中...</p>; if (error) return <p>出错了:{error.message}</p>; return ( <div> <h1>{data.user.name}</h1> <p>{data.user.email}</p> </div> ); }
然后在服务器端的page.tsx中导入使用:
// user/[userId]/page.tsx 无需添加'use client',保持服务器组件身份 import UserProfileClient from './UserProfileClient'; interface UserPageProps { params: { userId: string }; } export default function UserPage({ params }: UserPageProps) { return <UserProfileClient userId={params.userId} />; }
3. 调整Apollo Provider的位置
不要在服务器端的layouts.tsx中直接使用Apollo Provider,需要把它放到客户端专属的Provider组件里:
创建app/providers.tsx:
'use client'; import { ApolloProvider } from '@apollo/client'; import client from './apollo'; export function ApolloProviders({ children }: { children: React.ReactNode }) { return <ApolloProvider client={client}>{children}</ApolloProvider>; }
然后在根layout.tsx中引入:
// app/layout.tsx 服务器组件 import { ApolloProviders } from './providers'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="zh-CN"> <body> <ApolloProviders>{children}</ApolloProviders> </body> </html> ); }
核心注意事项
- 所有使用Apollo Hooks(
useQuery/useMutation等)的组件,必须添加'use client'指令。 - 服务器组件仅负责数据传递、页面结构渲染,不能调用任何客户端专属API或库。
- 初始化Apollo Client时,任何依赖浏览器环境的逻辑(如
localStorage、window)必须用typeof window !== 'undefined'做判断,避免在服务器端执行。
内容的提问来源于stack exchange,提问作者teilai teilai
相关产品推荐
相关产品推荐

