Next.js生产构建后react-quill编辑器工具栏不显示问题求助
Next.js生产构建下react-quill工具栏缺失问题修复
问题现象
- 开发环境:执行
next dev启动服务时,react-quill编辑器工具栏、编辑区渲染完全正常,所有功能可用 - 生产环境:执行
next build完成构建后,通过next start启动生产服务,编辑器工具栏无法正常显示,对应功能不可用 - 前置配置:已使用Next.js的
dynamicAPI动态引入编辑器组件,配置ssr: false关闭服务端渲染;集成quill-image-resize实现图片缩放能力,自定义了图片上传至S3的处理逻辑
核心复现代码
富文本编辑器组件(IBRichEditor/index.tsx)
import { useRef, useMemo, useState } from 'react' import ReactQuill, { Quill } from 'react-quill' import 'react-quill/dist/quill.snow.css' import { AxiosError } from 'axios' import { AuthAPIKit } from 'src/hooks/APITools/APIKit' import ImageResize from 'quill-image-resize' import { IBRichEditorProps } from './types' import { useSnackbar } from 'notistack' Quill.register('modules/ImageResize', ImageResize) const IBRichEditor = ({ defaultValue, onChangeEditor }: IBRichEditorProps) => { const QuillRef = useRef<ReactQuill>() const { enqueueSnackbar } = useSnackbar() const imageHandler = () => { const input = document.createElement('input') const formData = new FormData() let url = '' input.setAttribute('type', 'file') input.setAttribute('accept', 'image/*') input.click() input.onchange = async () => { const file = input.files if (file !== null) { formData.append('files', file[0]) try { const res = await AuthAPIKit.post('/s3Upload', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }) url = res.data.IBparams[0].Location const range = QuillRef.current?.getEditor().getSelection()?.index if (range !== null && range !== undefined) { let quill = QuillRef.current?.getEditor() quill?.setSelection(range, 1) quill?.clipboard.dangerouslyPasteHTML( range, `<img src=${url} alt="이미지 태그가 삽입됩니다." />`, ) } return { ...res, success: true } } catch (error) { const err = error as AxiosError return { ...err.response, success: false } } } } } const modules = useMemo( () => ({ toolbar: { container: [ ['bold', 'italic', 'underline', 'strike', 'blockquote'], [{ size: ['small', false, 'large', 'huge'] }, { color: [] }], [ { list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }, { align: [] }, ], ['image'], ], handlers: { image: imageHandler, }, }, ImageResize: { parchment: Quill.import('parchment'), }, }), [], ) return ( <> <ReactQuill ref={QuillRef} defaultValue={defaultValue} onChange={(content, delta, source, editor) => { onChangeEditor(editor.getHTML()) }} modules={modules} theme="snow" placeholder="test" /> </> ) } IBRichEditor.defaultProps = {} export default IBRichEditor
页面侧动态引入代码
import dynamic from 'next/dynamic' const IBRichEditor = dynamic( import('src/components/common/IBRichEditor/index'), { ssr: false }, ) const NewsUpdateModal = ({ handleClose, open, newsItem, newsList, setNewsList, }: NewsUpdateModalProps) => { // 其余业务逻辑省略 return ( <RegContentInputBox> <RegInputLabel>News Title</RegInputLabel> <RegContentInputQuill> <IBRichEditor defaultValue={inputData.content} onChangeEditor={onChangeEditor} /> </RegContentInputQuill> </RegContentInputBox> ) }
问题根因
- 生产构建默认开启Tree Shaking和代码压缩,会误判Quill内置工具栏依赖的图标、样式资源为未引用代码,直接从构建产物中剔除,导致工具栏只剩空DOM结构,没有样式和图标,视觉上呈现为工具栏缺失
Quill.register插件注册逻辑写在组件模块顶层,动态引入+生产压缩时存在模块加载时序问题:插件注册时Quill核心实例还未完成初始化,导致工具栏模块注册失败- 旧版
quill-image-resize包未适配ESM/CJS模块混用场景,生产构建下模块导出格式异常,会连带导致整个Quill模块初始化失败,工具栏配置不生效
修复步骤
按顺序操作即可解决问题:
- 调整动态引入配置,增加
suspense: true,确保组件仅在客户端环境完全加载后再初始化,避免加载时序问题:const IBRichEditor = dynamic( import('src/components/common/IBRichEditor/index'), { ssr: false, suspense: true }, ) - 移除组件顶层的插件注册、静态导入逻辑,将相关操作移到客户端挂载生命周期内执行,确保仅在浏览器环境下完成插件注册:
// 删掉顶层的 import ImageResize from 'quill-image-resize'、Quill.register代码 // 在组件内新增useEffect import { useEffect } from 'react' const IBRichEditor = ({ defaultValue, onChangeEditor }: IBRichEditorProps) => { const QuillRef = useRef<ReactQuill>() useEffect(() => { // 客户端挂载后再引入依赖、注册插件 const ReactQuill = require('react-quill') const Quill = ReactQuill.Quill const ImageResize = require('quill-image-resize').default Quill.register('modules/ImageResize', ImageResize) }, []) // 其余原有逻辑不变 } - 调整
modules配置的生成逻辑,将ImageResize依赖的parchment引入也放到客户端侧,不要在顶层调用Quill.import - 检查
next.config.js配置,不要把react-quill、quill加入transpilePackages列表,避免转译过程破坏Quill的模块结构;如果开启了swcMinify,不需要额外关闭,保持默认配置即可 - 如果以上操作后仍存在异常,直接替换
quill-image-resize为适配React生态的quill-image-resize-module-react,该包修复了生产环境模块兼容问题 - 操作完成后删除
.next缓存目录,重新执行next build && next start验证效果,避免旧构建缓存干扰结果
内容的提问来源于stack exchange,提问作者June
相关产品推荐
相关产品推荐

