NextJS动态导入未禁用服务端渲染 引入含window对象组件报错如何解决
你遇到的报错是两个核心原因导致的:
- React Hook调用规则违规:
useAgora是自定义React Hook,你将它放在if (isBrowser)条件块中调用,违反了「Hook必须在组件顶层无条件调用」的规则,会导致Hook执行时序混乱,就算条件判断成立也可能出现异常。 - Next.js渲染规则适配问题:如果你使用的是Next 13+ App Router,未给客户端组件加
'use client'指令的话,组件会默认被当做服务端组件处理,此时即便配置了ssr: false也可能在服务端被执行逻辑。另外你声明的async function Event()是非法的客户端组件写法,客户端组件不能为异步函数,会破坏动态导入的渲染逻辑。
步骤1:修复Event页面组件
首先移除页面组件的async声明,如果是App Router架构,在页面顶部添加'use client'指令:
// event.js 'use client' // App Router 必须加,Pages Router不用 import { useEffect, useRef, useState } from "react"; import dynamic from "next/dynamic"; const NoSsrLiveStreaming = dynamic( () => import("@/components/ui/LiveStreaming"), { ssr: false, // 可选:加个loading组件避免加载时闪动 loading: () => <div>加载中...</div> } ); // 移除async声明 function Event() { return ( <div suppressHydrationWarning={true}> <NoSsrLiveStreaming /> </div> ); } export default Event;
步骤2:修复LiveStreaming组件
首先确认isBrowser的实现是正确的:
const isBrowser = typeof window !== 'undefined'
然后调整Hook调用逻辑,不要在条件中调用useAgora,可以两种方案选其一:
方案A:修改useAgora内部做环境兼容
在useAgora自定义Hook的最开头加环境判断,非浏览器环境直接返回默认值:
// useAgora 钩子内部修改 function useAgora(client) { if (typeof window === 'undefined') { return { localAudioTrack: null, localVideoTrack: null, leave: () => {}, join: () => {}, joinState: false, remoteUsers: [] } } // 原有逻辑 }
之后调整LiveStreaming组件代码,移除Hook调用的条件包裹:
// LiveStreaming.js 'use client' // App Router必须加 import { useState, useEffect } from 'react' import AgoraRTC from 'agora-rtc-sdk' import useAgora from '你的useAgora路径' function LiveStreaming() { const [appid, setAppid] = useState(""); const [token, setToken] = useState(""); const [channel, setChannel] = useState(""); const [client, setClient] = useState(null); // 顶层无条件调用Hook const agoraObj = useAgora(client); const { localAudioTrack, localVideoTrack, leave, join, joinState, remoteUsers } = agoraObj; // client初始化放到useEffect中,仅客户端挂载后执行 useEffect(() => { if (!isBrowser) return const newClient = AgoraRTC.createClient({ codec: "h264", mode: "rtc" }); setClient(newClient) }, []) // 原有其他逻辑 } export default LiveStreaming
方案B:不用修改useAgora,把Agora相关逻辑全部放到子组件+useEffect中
如果你不想修改useAgora的代码,可以拆分组件,把用到useAgora的逻辑放到内层组件,仅在客户端挂载后才渲染内层组件:
// LiveStreaming.js 'use client' import { useState, useEffect } from 'react' function LiveStreaming() { const [isClient, setIsClient] = useState(false) useEffect(() => { setIsClient(true) }, []) // 未到客户端挂载阶段直接返回null if (!isClient) return null // 客户端阶段才渲染真正的直播逻辑,此时100%有window对象 return <LiveStreamingCore /> } // 内层核心组件,只有客户端才会渲染 function LiveStreamingCore() { const [appid, setAppid] = useState(""); const [token, setToken] = useState(""); const [channel, setChannel] = useState(""); // 这里直接初始化client和调用Hook,不需要条件判断,因为只会在客户端执行 const client = AgoraRTC.createClient({ codec: "h264", mode: "rtc" }); const agoraObj = useAgora(client); const { localAudioTrack, localVideoTrack, leave, join, joinState, remoteUsers } = agoraObj; // 原有逻辑 }
可选兜底方案:动态导入Agora SDK本身
如果还是有问题,可以直接把AgoraRTC的导入也改成动态导入,确保不会在服务端被加载:
useEffect(() => { const initAgora = async () => { const AgoraRTC = (await import('agora-rtc-sdk')).default // 后续初始化逻辑 } initAgora() }, [])
内容的提问来源于stack exchange,提问作者guest
相关产品推荐
相关产品推荐

