Next.js 13 Beta客户端组件出现document is not defined错误求助
解决Next.js 13中ReactQuill报"document is not defined"的问题
问题根源
在Next.js 13的App Router里,就算给组件加了'use client'标记,模块导入阶段的代码仍会在服务器端执行。ReactQuill在模块初始化时就调用了document对象,而服务器环境不存在document,所以直接抛出错误。你useEffect里的代码能正常运行,是因为useEffect本身只在客户端执行。
解决方案1:Next.js动态导入(推荐)
用next/dynamic动态导入ReactQuill并禁用SSR,确保组件仅在客户端加载执行:
'use client' import React, { useState } from 'react'; import dynamic from 'next/dynamic'; // 动态导入ReactQuill,禁用服务器端渲染 const ReactQuill = dynamic(() => import('react-quill'), { ssr: false, loading: () => <div>编辑器加载中...</div> // 可选:添加加载状态提示 }); // 客户端环境导入样式文件 import 'react-quill/dist/quill.snow.css'; const Editor = ({ placeholder }) => { const [editorHtml, setEditorHtml] = useState(''); const [theme, setTheme] = useState('snow'); useEffect(() => { console.log(document.location.href) }, []) return ( <div> <ReactQuill theme={theme} value={editorHtml} onChange={setEditorHtml} modules={Editor.modules} formats={Editor.formats} bounds={'.app'} placeholder={placeholder} /> </div> ); }; // 配置编辑器工具栏和格式 Editor.modules = { toolbar: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ 'list': 'ordered'}, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['link', 'image'], ['clean'] ] }; Editor.formats = [ 'bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block', 'header', 'list', 'color', 'background', 'link', 'image' ]; export default Editor;
解决方案2:条件渲染(仅客户端渲染组件)
通过状态控制ReactQuill仅在客户端挂载后渲染,避免服务器端执行组件代码:
'use client' import React, { useState, useEffect } from 'react'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; const Editor = ({ placeholder }) => { const [editorHtml, setEditorHtml] = useState(''); const [theme, setTheme] = useState('snow'); const [isClient, setIsClient] = useState(false); useEffect(() => { // 客户端挂载后标记为已在客户端环境 setIsClient(true); console.log(document.location.href) }, []) return ( <div> {/* 仅在客户端环境渲染ReactQuill */} {isClient && ( <ReactQuill theme={theme} value={editorHtml} onChange={setEditorHtml} modules={Editor.modules} formats={Editor.formats} bounds={'.app'} placeholder={placeholder} /> )} </div> ); }; // 配置编辑器工具栏和格式 Editor.modules = { toolbar: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ 'list': 'ordered'}, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['link', 'image'], ['clean'] ] }; Editor.formats = [ 'bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block', 'header', 'list', 'color', 'background', 'link', 'image' ]; export default Editor;
解决方案3:封装客户端专用组件
把ReactQuill封装到单独的客户端组件中,再通过动态导入使用:
- 创建
components/ClientQuill.tsx:
'use client'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; export default ReactQuill;
- 在
app/editor/page.tsx中动态导入:
'use client' import React, { useState } from 'react'; import dynamic from 'next/dynamic'; const ClientQuill = dynamic(() => import('@/components/ClientQuill'), { ssr: false, loading: () => <div>编辑器加载中...</div> }); const Editor = ({ placeholder }) => { const [editorHtml, setEditorHtml] = useState(''); const [theme, setTheme] = useState('snow'); useEffect(() => { console.log(document.location.href) }, []) return ( <div> <ClientQuill theme={theme} value={editorHtml} onChange={setEditorHtml} modules={Editor.modules} formats={Editor.formats} bounds={'.app'} placeholder={placeholder} /> </div> ); }; // 配置编辑器工具栏和格式 Editor.modules = { toolbar: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ 'list': 'ordered'}, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['link', 'image'], ['clean'] ] }; Editor.formats = [ 'bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block', 'header', 'list', 'color', 'background', 'link', 'image' ]; export default Editor;
内容的提问来源于stack exchange,提问作者kar
相关产品推荐
相关产品推荐

