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

Next.js中react-quill配置字体大小刷新报document未定义如何解决?

问题:Quill字体大小配置导致页面刷新崩溃

首次渲染组件时一切正常,但刷新页面后会触发document is not defined错误,注释掉以下代码即可解决,尤其是Quill.import('attributors/style/size')这一行:

const Size = Quill.import('attributors/style/size');
Size.whitelist = fontSizes;
Quill.register(Size, true);

请问如何正确添加字体大小,避免页面崩溃?


错误详情

error Error [ReferenceError]: document is not defined
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:7661:12)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:36:30)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:1030:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:36:30)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:5655:14)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:36:30)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:10045:13)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:36:30)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:11557:18)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:36:30)
at /home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:79:18
at /home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:82:10
at webpackUniversalModuleDefinition (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:9:20)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/quill/dist/quill.js:16:3)
at Module._compile (node:internal/modules/cjs/loader:1198:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
at Module.load (node:internal/modules/cjs/loader:1076:32)
at Function.Module._load (node:internal/modules/cjs/loader:911:12)
at Module.require (node:internal/modules/cjs/loader:1100:19)
at require (node:internal/modules/cjs/helpers:108:18)
at Object.<anonymous> (/home/pkarpinski/projects/content-generator/node_modules/react-quill/lib/index.js:43:31)
at Module._compile (node:internal/modules/cjs/loader:1198:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
at Module.load (node:internal/modules/cjs/loader:1076:32)
at Function.Module._load (node:internal/modules/cjs/loader:911:12)
at Module.require (node:internal/modules/cjs/loader:1100:19)
at require (node:internal/modules/cjs/helpers:108:18)
at Object.react-quill (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:1174:18)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/components/Editor/index.tsx:13:69)
at Object../src/components/Editor/index.tsx (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:215:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/components/Templates/BusinessMinute/TextEditor/TextEditorIntro/index.tsx:9:65)
at Object../src/components/Templates/BusinessMinute/TextEditor/TextEditorIntro/index.tsx (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:402:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/components/TemplateSwitcher/index.tsx:20:111)
at Object../src/components/TemplateSwitcher/index.tsx (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:270:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/components/Creator/SubjectForm.tsx:16:75)
at Function.__webpack_require__.a (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:97:13)
at eval (webpack-internal:///./src/components/Creator/SubjectForm.tsx:1:21)
at Object../src/components/Creator/SubjectForm.tsx (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:171:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/pages/creator.tsx:31:90)
at Function.__webpack_require__.a (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:97:13)
at eval (webpack-internal:///./src/pages/creator.tsx:1:21)
at Object../src/pages/creator.tsx (/home/pkarpinski/projects/content-generator/.next/server/pages/creator.js:446:1)
at __webpack_require__ (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./node_modules/next/dist/build/webpack/loaders/next-route-loader/index.js?kind=PAGES&page=%2Fcreator&preferredRegion=&absolutePagePath=.%2Fsrc%2Fpages%2Fcreator.tsx&absoluteAppPath=private-next-pages%2F_app&absoluteDocumentPath=private-next-pages%2F_document&middlewareConfigBase64=e30%3D!:23:80)
at Function.__webpack_require__.a (/home/pkarpinski/projects/content-generator/.next/server/webpack-runtime.js:97:13) {
digest: undefined
}

当前代码

import React, { useState } from 'react';
import dynamic from 'next/dynamic';
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
import 'react-quill/dist/quill.snow.css';
import { Quill } from 'react-quill';

const length = 14;
const factor = 20;

const fontSizes = new Array(length).fill(0).map((el, index) => (index + 1) * factor + 'px');

const Size = Quill.import('attributors/style/size');
Size.whitelist = fontSizes;
Quill.register(Size, true);

const Editor = (props) => {
    const [editorHtml, setEditorHtml] = useState('');

    const handleChange = (html) => {
        setEditorHtml(html);
    }

    return (
        <ReactQuill
            style={{
                height: '1200px',
                width: '1200px',
                backgroundColor: 'white',
            }}
            onChange={handleChange}
            value={editorHtml}
            modules={Editor.modules}
            formats={Editor.formats}
            placeholder={props.placeholder}
        />
    );
}

Editor.modules = {
    toolbar: [
        [{ 'header': '1' }, { 'header': '2' }, { 'font': [] }],
        [{ 'size': fontSizes }],
        ['bold', 'italic', 'underline', 'strike', 'blockquote'],
        [{ 'list': 'ordered' }, { 'list': 'bullet' },
        { 'indent': '-1' }, { 'indent': '+1' }],
        ['link', 'image', 'video'],
        ['clean']
    ],
    clipboard: {
        matchVisual: false,
    }
}

Editor.formats = [
    'header', 'font', 'size',
    'bold', 'italic', 'underline', 'strike', 'blockquote',
    'list', 'bullet', 'indent',
    'link', 'image', 'video'
]

export default Editor;

解决方案

问题原因

你用了Next.js的SSR,虽然给ReactQuill加了ssr: false的动态导入,但模块顶部直接导入Quill并执行Quill.import的代码会在SSR阶段运行——而服务器环境里没有document对象,Quill的样式attributor依赖浏览器环境,所以刷新时触发错误。

修复方法

必须把Quill的配置代码放到仅客户端运行的环境里,两种可行方式:

方式一:在动态导入时配置Quill

把字体大小的注册逻辑放到dynamic的导入回调里,因为ssr: false会让这个回调只在客户端执行:

import React, { useState } from 'react';
import dynamic from 'next/dynamic';
import 'react-quill/dist/quill.snow.css';

const length = 14;
const factor = 20;
const fontSizes = new Array(length).fill(0).map((el, index) => (index + 1) * factor + 'px');

// 动态导入时同时配置Quill
const ReactQuill = dynamic(async () => {
  const { default: RQ } = await import('react-quill');
  const { Quill } = await import('react-quill');
  
  const Size = Quill.import('attributors/style/size');
  Size.whitelist = fontSizes;
  Quill.register(Size, true);
  
  return RQ;
}, { ssr: false });

const Editor = (props) => {
    const [editorHtml, setEditorHtml] = useState('');

    const handleChange = (html) => {
        setEditorHtml(html);
    }

    return (
        <ReactQuill
            style={{
                height: '1200px',
                width: '1200px',
                backgroundColor: 'white',
            }}
            onChange={handleChange}
            value={editorHtml}
            modules={Editor.modules}
            formats={Editor.formats}
            placeholder={props.placeholder}
        />
    );
}

// 剩下的modules和formats配置不变
Editor.modules = {
    toolbar: [
        [{ 'header': '1' }, { 'header': '2' }, { 'font': [] }],
        [{ 'size': fontSizes }],
        ['bold', 'italic', 'underline', 'strike', 'blockquote'],
        [{ 'list': 'ordered' }, { 'list': 'bullet' },
        { 'indent': '-1' }, { 'indent': '+1' }],
        ['link', 'image', 'video'],
        ['clean']
    ],
    clipboard: {
        matchVisual: false,
    }
}

Editor.formats = [
    'header', 'font', 'size',
    'bold', 'italic', 'underline', 'strike', 'blockquote',
    'list', 'bullet', 'indent',
    'link', 'image', 'video'
]

export default Editor;

方式二:在组件的useEffect里配置

利用useEffect只在客户端执行的特性,在组件挂载后再配置Quill:

import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic';
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
import 'react-quill/dist/quill.snow.css';

const length = 14;
const factor = 20;
const fontSizes = new Array(length).fill(0).map((el, index) => (index + 1) * factor + 'px');

const Editor = (props) => {
    const [editorHtml, setEditorHtml] = useState('');

    useEffect(() => {
        // 仅在客户端加载并配置Quill
        const configureQuill = async () => {
            const { Quill } = await import('react-quill');
            const Size = Quill.import('attributors/style/size');
            Size.whitelist = fontSizes;
            Quill.register(Size, true);
        };
        configureQuill();
    }, []);

    const handleChange = (html) => {
        setEditorHtml(html);
    }

    return (
        <ReactQuill
            style={{
                height: '1200px',
                width: '1200px',
                backgroundColor: 'white',
            }}
            onChange={handleChange}
            value={editorHtml}
            modules={Editor.modules}
            formats={Editor.formats}
            placeholder={props.placeholder}
        />
    );
}

// 剩下的modules和formats配置不变
Editor.modules = {
    toolbar: [
        [{ 'header': '1' }, { 'header': '2' }, { 'font': [] }],
        [{ 'size': fontSizes }],
        ['bold', 'italic', 'underline', 'strike', 'blockquote'],
        [{ 'list': 'ordered' }, { 'list': 'bullet' },
        { 'indent': '-1' }, { 'indent': '+1' }],
        ['link', 'image', 'video'],
        ['clean']
    ],
    clipboard: {
        matchVisual: false,
    }
}

Editor.formats = [
    'header', 'font', 'size',
    'bold', 'italic', 'underline', 'strike', 'blockquote',
    'list', 'bullet', 'indent',
    'link', 'image', 'video'
]

export default Editor;

两种方法都能确保Quill的配置代码只在浏览器环境运行,避免SSR时的document未定义错误。


内容的提问来源于stack exchange,提问作者Patryk Karpiński

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:17:01