使用Base Web时Next.js报ReferenceError: document is not defined求助
问题:Next.js App Router中
ReferenceError: document is not defined(StyletronClient相关) - 试过很多关于
error ReferenceError: document is not defined的解决方案,但都不管用。之前这个问题被标记为重复,但旧方案无效,而且相关帖子已经是3年9个月前的了,React、Next.js以及SSR和客户端渲染的语法都变了很多。 - 终端报错信息:
- error node_modules/styletron-engine-atomic/dist-node-cjs/client/client.js (167:0) @ new StyletronClient - error ReferenceError: document is not defined at eval (./src/app/layout.js:15:16) at (sc_client)/./src/app/layout.js (/Users/anthonyweston/Sites/fogg/.next/server/app/page.js:2296:1) at __webpack_require__ (/Users/me/Sites/nextjs-baseweb/.next/server/webpack-runtime.js:33:42) null
- 看到其他问题提到
next/dynamic,但感觉不是我的解决方案?相关代码可复现问题。
解决方案
方法1:拆分客户端组件初始化StyletronClient
Next.js 13+ App Router默认组件是服务器组件,没法访问浏览器的document对象。所以要把Styletron的初始化放到客户端组件里:
- 新建一个客户端组件(比如
StyletronProviderClient.js),顶部加上"use client"声明; - 在这个组件里初始化
StyletronClient并提供上下文; - 在
layout.js里导入使用这个组件。
代码示例:
// src/components/StyletronProviderClient.js "use client"; import { StyletronProvider } from "styletron-react"; import { StyletronClient } from "styletron-engine-atomic"; const engine = new StyletronClient(); export default function StyletronProviderClient({ children }) { return <StyletronProvider value={engine}>{children}</StyletronProvider>; }
然后修改layout.js:
// src/app/layout.js import StyletronProviderClient from "@/components/StyletronProviderClient"; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <StyletronProviderClient>{children}</StyletronProviderClient> </body> </html> ); }
方法2:在useEffect中延迟初始化
如果不想拆分组件,可以给layout.js加上"use client",然后用useEffect延迟创建Styletron实例,避免服务器渲染时执行:
// src/app/layout.js "use client"; import { StyletronProvider } from "styletron-react"; import { StyletronClient } from "styletron-engine-atomic"; import { useEffect, useState } from "react"; export default function RootLayout({ children }) { const [engine, setEngine] = useState(null); useEffect(() => { setEngine(new StyletronClient()); }, []); if (!engine) return null; // 也可以换成加载中组件 return ( <html lang="en"> <body> <StyletronProvider value={engine}>{children}</StyletronProvider> </body> </html> ); }
关于next/dynamic的说明
next/dynamic主要是旧Pages Router里用来动态导入并禁用SSR的方式,在App Router里更推荐用"use client"指令来明确区分客户端组件,这是Next.js新架构的标准做法,比next/dynamic更适配当前的路由体系。
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

