Next.js集成ZegoCloud时出现document is not defined SSR错误求助
问题排查:Next.js + ZegoCloud SSR环境下document is not defined错误
问题描述
使用ZegoCloud音视频服务开发视频通话应用,基于Next.js框架,构建阶段页面出现服务端渲染(SSR)环境下的document is not defined错误,现有代码尝试通过isClient状态判断仍未解决。
问题根源
虽然添加了"use client"指令,但@zegocloud/zego-uikit-prebuilt包的顶层代码可能直接访问了浏览器环境的document/window对象,Next.js在SSR阶段仍会加载并执行该模块的初始化代码,导致报错。另外,直接将异步函数作为ref回调,在SSR阶段可能也会被触发执行,进一步引发问题。
解决方案
1. 动态导入ZegoUIKitPrebuilt(禁用SSR)
使用Next.js的dynamic函数导入Zego组件,确保该模块仅在客户端加载,避免SSR阶段执行。
2. 调整ref与初始化逻辑
将异步初始化逻辑移到useEffect中,配合ref引用,确保仅在客户端组件挂载后执行初始化操作。
修改后的完整代码
"use client"; import { useSession } from "next-auth/react"; import { useSearchParams } from "next/navigation"; import { generateToken04 } from "@/config/token"; import { useEffect, useState, useRef } from "react"; import dynamic from "next/dynamic"; // 动态导入ZegoUIKitPrebuilt,禁用SSR const ZegoUIKitPrebuilt = dynamic( () => import("@zegocloud/zego-uikit-prebuilt"), { ssr: false } ); function randomID(len) { let result = ''; const chars = '12345qwertyuiopasdfgh67890jklmnbvcxzMNBVCZXASDQWERTYHGFUIOLKJP'; const maxPos = chars.length; len = len || 5; for (let i = 0; i < len; i++) { result += chars.charAt(Math.floor(Math.random() * maxPos)); } return result; } function Page() { const { data } = useSession(); const roomID = useSearchParams()?.get("roomId"); const userID = randomID(5); const containerRef = useRef(null); useEffect(() => { // 确保仅在客户端且容器存在时执行初始化 const initMeeting = async () => { if (!containerRef.current || !roomID) return; const appID = parseInt(process.env.NEXT_PUBLIC_ZEGO_APP_ID); const serverSecret = process.env.NEXT_PUBLIC_ZEGO_SERVER_SECRET; const experationTime = 3600; try { const token = await generateToken04(appID, userID, serverSecret, experationTime); const kitToken = ZegoUIKitPrebuilt.generateKitTokenForProduction( appID, token, roomID, userID, data?.user?.name ); const zp = ZegoUIKitPrebuilt.create(kitToken); zp.joinRoom({ container: containerRef.current, sharedLinks: [ { name: '复制链接', url: `${window.location.origin}${window.location.pathname}?roomId=${roomID}`, }, ], scenario: { mode: ZegoUIKitPrebuilt.GroupCall, }, }); } catch (error) { console.error(error); } }; initMeeting(); }, [roomID, userID, data]); // 服务端渲染时返回加载占位 if (typeof window === 'undefined') { return <div className="w-screen h-screen">加载中...</div>; } return ( <div className="-ml-14" ref={containerRef} style={{ width: "100vw", height: "100vh" }} ></div> ); } export default Page;
关键修改点说明
- 动态导入Zego组件:通过
dynamic函数并设置ssr: false,确保Zego的SDK仅在客户端环境加载,避免SSR阶段执行包含浏览器API的代码。 - 使用useRef配合useEffect:将容器引用和初始化逻辑分离,
useEffect保证仅在客户端组件挂载后执行异步初始化操作,替代原来的ref回调(避免SSR阶段触发)。 - 直接判断window存在:用
typeof window === 'undefined'更直接地检测是否处于SSR环境,替代isClient状态,减少不必要的状态更新。
内容的提问来源于stack exchange,提问作者Bhavesh Choudhary
相关产品推荐
相关产品推荐

