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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:36:20