如何在Next.js的ReactQuill中用内联样式替代Quill类
让react-quill-new生成内联样式适配HTML邮件
在Next.js项目中使用react-quill-new编辑器时,需要生成内联样式(如style="text-align: center;")而非Quill默认的类样式(如ql-align-center),因为HTML邮件客户端无法识别外部类样式。此前尝试设置clipboard.matchVisual: false和指定formats属性均未生效,生成的HTML仍包含类样式。
当前Editor组件代码
'use client' import dynamic from "next/dynamic"; import "./EditorStyle/bubble.css"; import Quill from "quill"; const ReactQuill = dynamic(() => import("react-quill-new"), { ssr: false }); export const Editor = ({ content, onChange, className }: { content: any, className: string, onChange: (value: string) => void }) => { const module = { toolbar: { container: [ [{ 'header': [1, 2, 3, 4, 5, 6, false] }], ["bold", "italic", "underline"], ['link'], [{ 'list': 'ordered' }, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['clean'], [{ 'font': [] }], [{ 'align': [] }], ], handlers: { link: function(value: string) { const that: any = this; const tooltip = that.quill.theme.tooltip; const input = tooltip.root.querySelector("input[data-link]"); input.dataset.link = "google.com"; if (value) { const range = that.quill.getSelection(); if (range == null || range.length === 0) { return; } let preview = ''; if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf("mailto:") !== 0) { preview = `mailto:${preview}`; } const { tooltip } = that.quill.theme; tooltip.edit("link", preview); } else { that.quill.format("link", false); } } } }, clipboard: { matchVisual: false } }; return ( <div> <div> <ReactQuill theme="bubble" value={content} modules={module} placeholder="Write something amazing..." onChange={onChange} className={`${className}`} /> </div> </div> ); };
需求
- 对齐样式(居中、左/右对齐)使用内联
style属性,而非ql-align-*类 - 列表、字体、颜色等所有样式均采用内联样式呈现
解决方案
通过自定义Quill格式扩展,替换默认的类样式逻辑为内联样式设置。在组件初始化时注册这些自定义格式,覆盖Quill的默认行为:
1. 注册自定义格式
在组件内部添加格式注册代码(确保在ReactQuill渲染前执行):
// 注册自定义格式,覆盖Quill默认行为 (() => { // 对齐格式:替换类为text-align内联样式 Quill.register({ 'formats/align': class Align extends Quill.import('formats/align') { static formats(node: HTMLElement) { const textAlign = node.style.textAlign; return textAlign ? textAlign : undefined; } format(name: string, value: string) { if (value) { this.domNode.style.textAlign = value; } else { delete this.domNode.style.textAlign; } // 移除Quill默认添加的对齐类 this.domNode.classList.remove(...Array.from(this.domNode.classList).filter(cls => cls.startsWith('ql-align-'))); } } }, true); // 字体格式:替换类为font-family内联样式 Quill.register({ 'formats/font': class Font extends Quill.import('formats/font') { format(name: string, value: string) { if (value) { this.domNode.style.fontFamily = value; } else { delete this.domNode.style.fontFamily; } // 移除Quill默认添加的字体类 this.domNode.classList.remove(...Array.from(this.domNode.classList).filter(cls => cls.startsWith('ql-font-'))); } } }, true); // 列表格式:移除默认类并添加内联缩进样式 Quill.register({ 'formats/list': class List extends Quill.import('formats/list') { static formats(node: HTMLElement) { return node.tagName === 'OL' ? 'ordered' : node.tagName === 'UL' ? 'bullet' : undefined; } format(name: string, value: string) { super.format(name, value); // 移除Quill默认添加的列表类 this.domNode.classList.remove('ql-list', `ql-${value}`); // 添加邮件友好的内联缩进 if (value === 'ordered' || value === 'bullet') { this.domNode.style.marginLeft = '2em'; } else { delete this.domNode.style.marginLeft; } } } }, true); })();
2. 整合后的完整组件
将上述注册代码插入原组件中,最终代码如下:
'use client' import dynamic from "next/dynamic"; import "./EditorStyle/bubble.css"; import Quill from "quill"; const ReactQuill = dynamic(() => import("react-quill-new"), { ssr: false }); // 注册自定义格式,覆盖Quill默认行为 (() => { // 对齐格式 Quill.register({ 'formats/align': class Align extends Quill.import('formats/align') { static formats(node: HTMLElement) { const textAlign = node.style.textAlign; return textAlign ? textAlign : undefined; } format(name: string, value: string) { if (value) { this.domNode.style.textAlign = value; } else { delete this.domNode.style.textAlign; } this.domNode.classList.remove(...Array.from(this.domNode.classList).filter(cls => cls.startsWith('ql-align-'))); } } }, true); // 字体格式 Quill.register({ 'formats/font': class Font extends Quill.import('formats/font') { format(name: string, value: string) { if (value) { this.domNode.style.fontFamily = value; } else { delete this.domNode.style.fontFamily; } this.domNode.classList.remove(...Array.from(this.domNode.classList).filter(cls => cls.startsWith('ql-font-'))); } } }, true); // 列表格式 Quill.register({ 'formats/list': class List extends Quill.import('formats/list') { static formats(node: HTMLElement) { return node.tagName === 'OL' ? 'ordered' : node.tagName === 'UL' ? 'bullet' : undefined; } format(name: string, value: string) { super.format(name, value); this.domNode.classList.remove('ql-list', `ql-${value}`); if (value === 'ordered' || value === 'bullet') { this.domNode.style.marginLeft = '2em'; } else { delete this.domNode.style.marginLeft; } } } }, true); })(); export const Editor = ({ content, onChange, className }: { content: any, className: string, onChange: (value: string) => void }) => { const module = { toolbar: { container: [ [{ 'header': [1, 2, 3, 4, 5, 6, false] }], ["bold", "italic", "underline"], ['link'], [{ 'list': 'ordered' }, { 'list': 'bullet' }], [{ 'color': [] }, { 'background': [] }], ['clean'], [{ 'font': [] }], [{ 'align': [] }], ], handlers: { link: function(value: string) { const that: any = this; const tooltip = that.quill.theme.tooltip; const input = tooltip.root.querySelector("input[data-link]"); input.dataset.link = "google.com"; if (value) { const range = that.quill.getSelection(); if (range == null || range.length === 0) { return; } let preview = ''; if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf("mailto:") !== 0) { preview = `mailto:${preview}`; } const { tooltip } = that.quill.theme; tooltip.edit("link", preview); } else { that.quill.format("link", false); } } } }, clipboard: { matchVisual: false } }; return ( <div> <div> <ReactQuill theme="bubble" value={content} modules={module} placeholder="Write something amazing..." onChange={onChange} className={`${className}`} /> </div> </div> ); };
补充说明
- 颜色、背景色格式Quill默认已使用内联样式,无需额外修改
- 标题(header)格式默认使用
<h1>-<h6>标签,邮件客户端原生支持 - 加粗、斜体、下划线默认使用
<b>/<strong>、<i>/<em>、<u>标签,符合邮件规范
内容的提问来源于stack exchange,提问作者Ratnesh
相关产品推荐
相关产品推荐

