Next.js仅首次渲染使用HOC的页面时出现process未定义报错
问题根因
Next.js 中 getInitialProps 会同时在服务端和客户端执行:
- 直接刷新页面、首次输入地址访问时,
getInitialProps在服务端运行,能正常读取 Node 环境的process变量,所以访问正常 - 当通过客户端路由(比如Next.js的
Link组件跳转)进入页面时,getInitialProps会在浏览器环境运行,浏览器不存在process全局变量,就会抛出你遇到的报错。
你之前用getServerSideProps没有问题,是因为getServerSideProps只会在服务端执行,永远不会跑到客户端运行。
解决方案1:改用getServerSideProps封装逻辑(推荐)
既然你之前用getServerSideProps没有问题,优先选择这个方案,避免客户端执行的问题,HOC 可以调整为合并页面原有getServerSideProps和环境变量读取逻辑:
// src/components/app/withEnvironment.tsx import { EnvironmentValues, readPublicEnvironmentValues, } from 'config/environment'; import { NextPage, GetServerSideProps } from 'next'; import { EnvironmentProvider } from 'providers'; export function withEnvironment<TProps extends {}>( Page: NextPage<TProps>, pageGetServerSideProps?: GetServerSideProps<TProps> ): NextPage<TProps & {environmentValues: EnvironmentValues}> { const PageWithEnvironment = ({ environmentValues, ...props }: TProps & {environmentValues: EnvironmentValues}) => { return ( <EnvironmentProvider value={environmentValues}> <Page {...props as TProps} /> </EnvironmentProvider> ); }; // 封装getServerSideProps,只会在服务端执行 const getServerSideProps: GetServerSideProps = async (ctx) => { const environmentValues = readPublicEnvironmentValues(); // 合并原页面的getServerSideProps返回值 if (pageGetServerSideProps) { const pageRes = await pageGetServerSideProps(ctx); if ('props' in pageRes) { return { ...pageRes, props: { ...pageRes.props, environmentValues } } } return pageRes; } return { props: { environmentValues } } } (PageWithEnvironment as NextPage).getServerSideProps = getServerSideProps; return PageWithEnvironment; }
页面使用的时候可以传入自己的getServerSideProps:
// src/pages/change-sets/index.tsx import {withEnvironment} from 'components/app'; import {useEnvironment} from 'providers' function ChangeSetPage(): JSX.Element { const environmentValues = useEnvironment(); return ( <div> <h1>ChangeSetPage</h1> </div> ); } // 页面如果有自己的getServerSideProps可以作为第二个参数传入 export default withEnvironment(ChangeSetPage/*, yourPageGetServerSideProps */);
解决方案2:兼容getInitialProps的执行环境
如果你一定要保留getInitialProps的写法,可以在执行读取环境变量逻辑前判断当前运行环境,同时把服务端读取到的环境变量同步到客户端:
- 先调整
getInitialProps的逻辑,判断是否是服务端环境:
(PageWithEnvironment as NextPage).getInitialProps = async (ctx) => { let environmentValues: EnvironmentValues; // 有req对象说明是服务端执行 if (ctx.req) { environmentValues = readPublicEnvironmentValues(); } else { // 客户端直接从window上取服务端注入的环境变量 environmentValues = window.__ENVIRONMENT_VALUES__; } // 合并原页面的getInitialProps返回值 const pageInitialProps = Page.getInitialProps ? await Page.getInitialProps(ctx) : {}; return { ...pageInitialProps, environmentValues }; };
- 在自定义
_document.tsx中把环境变量注入到客户端window对象:
// src/pages/_document.tsx import Document, { Html, Head, Main, NextScript } from 'next/document' import { readPublicEnvironmentValues } from 'config/environment' class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx) const environmentValues = readPublicEnvironmentValues() return { ...initialProps, environmentValues } } render() { return ( <Html> <Head /> <body> <script dangerouslySetInnerHTML={{ __html: `window.__ENVIRONMENT_VALUES__ = ${JSON.stringify(this.props.environmentValues)}` }} /> <Main /> <NextScript /> </body> </Html> ) } } export default MyDocument
注意事项
- 所有需要在客户端访问的环境变量,记得加上
NEXT_PUBLIC_前缀,Next.js会自动把这类变量注入到客户端代码中,不需要额外处理就能直接访问 - 你原来的HOC直接覆盖了
getInitialProps,如果原页面本身定义了getInitialProps,原有逻辑会丢失,上面的示例代码已经补充了合并原页面getInitialProps/getServerSideProps的逻辑。
内容的提问来源于stack exchange,提问作者Herber230
相关产品推荐
相关产品推荐

