Next.js 13 App Router构建报错:navigator is not defined
问题分析
即使页面添加了'use client'标记,Next.js执行next build时仍会对组件进行服务器端预渲染(或静态生成),组件的顶层代码会在服务器环境中执行,而服务器环境不存在navigator这个浏览器专属对象,因此触发报错。npm run dev时无异常是因为开发模式下更多依赖客户端即时渲染,不会严格执行服务器端预检查。
解决方案
1. 将依赖navigator的代码放入useEffect
useEffect的回调函数仅在客户端渲染完成后执行,完全避免服务器端执行浏览器专属API:
'use client'; import { useEffect, useState } from 'react'; export default function VideoChatPage() { const [mediaStream, setMediaStream] = useState<MediaStream | null>(null); useEffect(() => { // 仅在客户端执行媒体流获取逻辑 const fetchMediaStream = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); setMediaStream(stream); } catch (error) { console.error('获取媒体流失败:', error); } }; fetchMediaStream(); // 组件卸载时释放媒体流 return () => { if (mediaStream) { mediaStream.getTracks().forEach(track => track.stop()); } }; }, []); return ( <div> {mediaStream && ( <video autoPlay muted ref={el => el && (el.srcObject = mediaStream)} /> )} </div> ); }
2. 用条件判断包裹navigator相关代码
如果必须在顶层执行部分逻辑,可通过typeof navigator !== 'undefined'确保仅在客户端环境运行:
'use client'; // 顶层条件判断,避免服务器端执行 const isClient = typeof navigator !== 'undefined'; export default function VideoChatPage() { useEffect(() => { if (isClient) { // 执行依赖navigator的逻辑 } }, []); return <div>...</div>; }
关键注意点
- 不要在组件顶层直接调用
navigator.mediaDevices或其他浏览器专属API,即使加了'use client'也不行 - 所有依赖浏览器环境的逻辑,都要放到
useEffect、useLayoutEffect或客户端事件回调(如点击事件)中 - 组件卸载时记得释放媒体资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Egbo
相关产品推荐
相关产品推荐

