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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:52:39