Next.js v13中如何在'use client'组件中使用.env变量?
在Next.js v13中解决客户端Apollo组件访问.env变量的问题
你当前的Apollo包装组件标记了'use client',无法直接读取未加NEXT_PUBLIC_前缀的.env变量——这是Next.js的默认规则:只有带NEXT_PUBLIC_前缀的环境变量会被暴露给客户端代码。针对你的场景,有两种可行解决方法:
方案1:使用NEXT_PUBLIC_前缀的环境变量(适合非敏感配置)
如果你的GraphQL地址和访问令牌不需要保密(比如公开的客户端令牌),可以直接给.env变量添加NEXT_PUBLIC_前缀:
- 修改.env文件:
NEXT_PUBLIC_GRAPHQL_ENDPOINT=你的GraphQL服务地址 NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN=你的客户端访问令牌
- 更新组件中的变量读取逻辑:
'use client'; import { ApolloLink, HttpLink } from '@apollo/client'; import { ApolloNextAppProvider, NextSSRInMemoryCache, NextSSRApolloClient, SSRMultipartLink, } from '@apollo/experimental-nextjs-app-support/ssr'; // 读取带NEXT_PUBLIC_前缀的变量 const { NEXT_PUBLIC_GRAPHQL_ENDPOINT, NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN } = process.env; export function makeClient() { const httpLink = new HttpLink({ uri: NEXT_PUBLIC_GRAPHQL_ENDPOINT, headers: { authorization: NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN ? `Bearer ${NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN}` : '', }, fetchOptions: { cache: 'no-store' }, }); return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: typeof window === 'undefined' ? ApolloLink.from([ new SSRMultipartLink({ stripDefer: true, }), httpLink, ]) : httpLink, }); } export function ApolloWrapper({ children }: React.PropsWithChildren) { return <ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>; }
方案2:通过服务器组件传递配置(更安全,适合敏感令牌)
如果你的访问令牌是敏感信息(不能暴露给客户端),推荐用这种方法:利用服务器组件可以直接读取所有.env变量的特性,将配置作为props传递给客户端的ApolloWrapper。
- 修改ApolloWrapper组件,接收配置参数:
'use client'; import { ApolloLink, HttpLink } from '@apollo/client'; import { ApolloNextAppProvider, NextSSRInMemoryCache, NextSSRApolloClient, SSRMultipartLink, } from '@apollo/experimental-nextjs-app-support/ssr'; // 定义配置类型 type ApolloConfig = { graphqlEndpoint: string; contentfulAccessToken: string; }; // 让makeClient接收配置参数 export function makeClient(config: ApolloConfig) { const httpLink = new HttpLink({ uri: config.graphqlEndpoint, headers: { authorization: config.contentfulAccessToken ? `Bearer ${config.contentfulAccessToken}` : '', }, fetchOptions: { cache: 'no-store' }, }); return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: typeof window === 'undefined' ? ApolloLink.from([ new SSRMultipartLink({ stripDefer: true, }), httpLink, ]) : httpLink, }); } // ApolloWrapper接收config props export function ApolloWrapper({ children, config }: React.PropsWithChildren<{ config: ApolloConfig }>) { return <ApolloNextAppProvider makeClient={() => makeClient(config)}>{children}</ApolloNextAppProvider>; }
- 在服务器组件(比如
app/layout.tsx)中引入ApolloWrapper并传递配置:
// layout.tsx 默认是服务器组件,无需标记'use client' import { ApolloWrapper } from './apollo-wrapper'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { // 服务器组件可以直接读取所有.env变量 const apolloConfig = { graphqlEndpoint: process.env.GRAPHQL_ENDPOINT!, contentfulAccessToken: process.env.CONTENTFUL_ACCESS_TOKEN!, }; return ( <html lang="en"> <body> <ApolloWrapper config={apolloConfig}>{children}</ApolloWrapper> </body> </html> ); }
注意事项
- 敏感令牌(如服务端专属的访问密钥)绝对不能使用方案1,否则会被打包到客户端代码中,造成安全风险。
- 方案2中,Apollo的
NextSSRApolloClient会自动处理SSR和客户端的状态同步,令牌只会在服务器请求和客户端内部请求中使用,不会暴露到浏览器的开发者工具中。
内容的提问来源于stack exchange,提问作者John williams
相关产品推荐
相关产品推荐

