一、Vue3项目为啥优先选Quill做富文本?
做前端项目时,富文本编辑需求太常见了!从简单的文章排版到复杂的富媒体嵌入,选对工具能省不少事儿,要是你用Vue3开发,想结合Quill实现富文本功能,肯定一堆疑问:选Quill有啥优势?咋快速集成?自定义工具栏、加拓展功能咋搞?性能和兼容性坑咋填?今天咱就把这些常见问题掰开揉碎,用白话讲明白~
先唠唠富文本编辑器的选型逻辑,市面上常见的有TinyMCE、Slate、Quill这些,Quill能在Vue3项目里“杀出重围”,核心是这几点:
开发体验友好,API灵活度高
Quill用Delta文档模型,所有编辑操作(加粗、插入图片)都会生成Delta格式的JSON数据,举个例子,一段“Hello Quill”的内容,Delta长这样:
{ "ops": [{"insert": "Hello", "attributes": {"bold": true}}, {"insert": " Quill"}]}
这种结构化的数据,在Vue3里处理双向绑定、做版本控制(比如撤销重做)特别顺手,而且Quill暴露的API很直观,像quill.insertText()、quill.getContents()这些方法,哪怕是刚接触的前端同学,看文档也能快速上手。
可扩展性拉满,靠“模块”自由拼功能
Quill的Module机制是灵魂!想加代码高亮?装个quill-syntax模块;要图片能拖拽缩放?用quill-image-resize-module;甚至想做协同编辑,社区也有现成方案,Vue3的Composition API刚好能把这些模块的逻辑封装成可复用的函数(比如useQuillUpload处理图片上传),项目里多处用富文本时,不用重复写代码。
轻量+生态,中小项目“性价比”首选
Quill核心包体积很小,只做基础富文本编辑完全够用,要是项目需要复杂功能,社区插件能补上——比如数学公式用quill-mathjax、表格用quill-table,对比TinyMCE(功能全但体积大,适合大型CMS)、Slate(高度灵活但需要自己造很多轮子),Quill在“快速落地需求”和“定制性”之间平衡得很好,中小项目选它不踩坑。
Vue3里咋最快把Quill用起来?
很多同学一上来就想“快速跑通流程”,这部分直接给三步落地法,哪怕是Vue3新手也能跟着做:
步骤1:装依赖,选对社区库
Vue3生态里,最常用的Quill封装库是vue3-quill(注意不是老版本的vue-quill-editor,那是Vue2的),命令行装包:
npm i vue3-quill
步骤2:封装基础富文本组件
用Vue3的<script setup>语法,写个最简化的编辑器组件:
<template>
<!-- QuillEditor是vue3-quill提供的组件 -->
<QuillEditor
v-model="content"
:options="editorOptions"
@ready="onEditorReady"
/>
</template>
<script setup>
import { ref } from 'vue'
import { QuillEditor } from 'vue3-quill' // 引入组件
import 'vue3-quill/dist/style.css' // 引入默认样式
// 双向绑定的内容
const content = ref('')
// 编辑器配置项
const editorOptions = {
theme: 'snow', // snow是带工具栏的主题,bubble是气泡式(聚焦时显示工具栏)
modules: {
toolbar: [ // 配置默认显示哪些按钮
['bold', 'italic', 'underline'], // 格式类
['link', 'image'], // 媒体类
[{ list: 'ordered' }, { list: 'bullet' }] // 列表类
]
}
}
// 编辑器初始化完成后触发,能拿到Quill实例
const onEditorReady = (quill) => {
console.log('Quill实例:', quill)
}
</script>
这段代码做了这些事:
- 用
v-model双向绑定(content变化时编辑器自动更新,编辑器内容变化时content也自动更新); - 通过
options配置主题和工具栏; - 监听
ready事件,拿到Quill实例后能调用API(比如quill.insertEmbed()插入图片)。
步骤3:验证效果,快速调试
启动Vue3项目(比如npm run dev),打开页面就能看到带工具栏的富文本编辑器,试试点“加粗”按钮,输入内容是否变粗;点“图片”按钮,能不能插入图片(默认是base64格式,后面会讲改成服务器上传),如果没渲染出来,先检查这几点:
- 依赖是否装对(
package.json里有vue3-quill); - 样式是否引入(漏了
import 'vue3-quill/dist/style.css'会导致工具栏样式乱掉); - Vue3的组件引入是否正确(别把
QuillEditor导入路径写错)。
Quill工具栏能自定义到啥程度?咋改?
默认工具栏满足不了需求?比如想加“插入签名”按钮、把图片上传改成走后端接口、自定义字体选项……这部分教你从基础配置到深度自定义:
基础配置:用数组/对象改默认按钮
Quill的toolbar支持两种配置方式:
-
数组形式:适合快速开关功能,比如只保留加粗、链接:
modules: { toolbar: [['bold'], ['link']] } -
对象形式:适合精细控制,比如标题只保留1、2级,隐藏3级及以上:
modules: { toolbar: { container: [ [{ header: [1, 2, false] }] // false表示“不显示标题”选项 ] } }
进阶:加自定义按钮(插入签名”)
想加个点击后插入“【个人签名】”的按钮,步骤分三步:
第一步:在toolbar里声明自定义按钮
给container加一个自定义标识(比如'custom-sign'):
modules: {
toolbar: {
container: [
['bold', 'italic'],
['custom-sign'] // 新增自定义按钮
]
}
}
第二步:给按钮绑定点击逻辑(handlers)
用handlers配置按钮的点击事件,逻辑里调用Quill API插入内容:
modules: {
toolbar: {
container: [...],
handlers: {
'custom-sign': function() {
// this.quill 是当前Quill实例
const quill = this.quill
// 获取光标位置,插入文本
const cursorPos = quill.getSelection().index
quill.insertText(cursorPos, '【个人签名】', 'bold', true)
}
}
}
}
第三步:给按钮加样式(图标/文字)
Quill的工具栏按钮默认是文字,想改成图标?用CSS的伪元素:
/* 找到自定义按钮的类:.ql- + 自定义标识 */
.ql-custom-sign::before {
content: '✒️'; /* 用Unicode图标,也可以用图片url */
font-size: 16px;
}
高阶:改造图片上传(从base64到服务器上传)
Quill默认插入图片是转base64,要是项目里图片要存服务器,得重写图片处理逻辑:
第一步:拦截toolbar的“image”按钮事件
在handlers里重写image的逻辑,改成触发文件选择框:
modules: {
toolbar: {
container: [..., ['image']],
handlers: {
image: function() {
// 创建隐藏的文件输入框
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*' // 只允许图片
// 选择文件后触发上传
input.addEventListener('change', (e) => {
const file = e.target.files[0]
// 调后端上传接口(这里用axios举例)
uploadImage(file).then(res => {
const imgUrl = res.data.url // 假设接口返回图片URL
// 插入图片到编辑器
this.quill.insertEmbed(
this.quill.getSelection().index,
'image',
imgUrl
)
})
})
// 触发文件选择框
input.click()
}
}
}
}
第二步:处理粘贴图片(用户从剪贴板粘贴图片)
除了点击按钮上传,还要处理“粘贴图片”的场景,监听Quill的paste事件,拦截粘贴内容:
// 在onEditorReady里绑定事件
const onEditorReady = (quill) => {
quill.on('paste', (e) => {
e.preventDefault() // 阻止默认粘贴行为(默认是转base64)
const clipboardData = e.clipboardData || window.clipboardData
const files = clipboardData.files
if (files.length > 0 && files[0].type.startsWith('image/')) {
// 同样调上传接口,把图片URL插入
uploadImage(files[0]).then(res => {
quill.insertEmbed(quill.getSelection().index, 'image', res.data.url)
})
}
})
}
Quill在Vue3里咋拓展复杂功能?
项目需求一复杂,只靠默认功能肯定不够,这里举代码高亮、数学公式、版本控制三个典型场景,教你用Quill的模块机制拓展:
场景1:代码块高亮(结合PrismJS)
想让编辑器里的代码块自动高亮?步骤如下:
第一步:装PrismJS和样式
npm i prismjs
然后引入默认样式(也可以选其他主题):
import 'prismjs/themes/prism.css'
第二步:自定义高亮逻辑
写个函数,遍历编辑器里的代码块,用PrismJS高亮:
import Prism from 'prismjs'
const highlightCode = (quill) => {
// 找到所有代码块(Quill里代码块是<pre class="ql-syntax">)
const codeBlocks = quill.root.querySelectorAll('pre.ql-syntax')
codeBlocks.forEach(block => {
const codeEl = block.querySelector('code')
// 从class里提取语言(比如class="language-js" → 语言是js)
const lang = codeEl.className.match(/language-(\w+)/)?.[1] || 'javascript'
// 调用Prism高亮
Prism.highlightElement(codeEl, Prism.languages[lang])
})
}
第三步:绑定编辑器事件
在onEditorReady里,监听text-change变化时触发高亮):
const onEditorReady = (quill) => {
quill.on('text-change', (delta, oldDelta, source) => {
if (source === 'user') { // 只处理用户主动修改的情况
highlightCode(quill)
}
})
}
场景2:数学公式编辑(结合Katex)
想在富文本里插入数学公式($E=mc^2$$),用Katex渲染:
第一步:装Katex和Quill插件
npm i katex quill-mathjax
第二步:配置Quill的mathjax模块
在editorOptions里加mathjax配置,让工具栏显示公式按钮:
import 'katex/dist/katex.css'
import MathJax from 'quill-mathjax'
const editorOptions = {
modules: {
mathjax: {
config: (() => {
// Katex配置,比如默认行内公式、块级公式
window.MathJax = {
tex: {
inlineMath: [['$', '$']],
displayMath: [['$$', '$$']]
}
}
return window.MathJax
})(),
editor: MathJax
},
toolbar: [['mathjax']] // 工具栏加公式按钮
}
}
点击“公式”按钮后,会弹出输入框,输入LaTeX语法(比如E=mc^2),编辑器会自动渲染成公式。
场景3:版本控制(撤销/重做)
Quill内置history模块,能记录内容变化,实现撤销(undo)、重做(redo),配置起来很简单:
const editorOptions = {
modules: {
history: {
delay: 2000, // 每2秒记录一次历史(避免太频繁)
maxStack: 50, // 最多存50步历史
userOnly: true // 只记录用户主动操作,忽略程序自动修改
},
toolbar: [['undo', 'redo']] // 工具栏加撤销/重做按钮
}
}
要是想在Vue组件里自己加按钮控制撤销/重做,拿到Quill实例后调用API:
// 假设按钮的点击事件
const handleUndo = () => {
quill.value.history.undo() // quill.value是保存的Quill实例
}
const handleRedo = () => {
quill.value.history.redo()
}
Vue3下Quill性能咋优化?
多了(比如几千字+大量图片),容易出现卡顿,这部分给三个实战优化思路:
大数据量:防抖+Delta优化
当用户快速输入时,Quill的text-change事件会频繁触发,导致性能下降,用防抖限制事件处理频率:
import { debounce } from 'lodash-es'
const onEditorReady = (quill) => {
// 防抖处理高亮/上传等逻辑
const debouncedHighlight = debounce(() => {
highlightCode(quill)
}, 300) // 300毫秒内只执行一次
quill.on('text-change', debouncedHighlight)
}
尽量用Quill的Delta API做“增量更新”,而不是全量替换内容,比如要修改一段文字,用quill.updateContents(delta)而不是quill.setContents()。
模块懒加载:按需加载大依赖
像PrismJS、Katex这些库体积不小,要是页面初始化时就加载,会拖慢首屏,用动态导入(import())实现懒加载:
const onEditorReady = (quill) => {
// 需要代码高亮时再加载Prism
const loadPrism = async () => {
const { default: Prism } = await import('prismjs')
// 初始化高亮逻辑...
}
// 比如用户点击“代码块”按钮后加载
quill.getModule('toolbar').addHandler('code-block', () => {
loadPrism().then(() => {
// 加载完成后插入代码块
quill.insertText(quill.getSelection().index, '```js\n// 请输入代码\n```', 'code-block')
})
})
}
组件销毁:及时清理Quill实例
Vue3组件销毁时,要是没销毁Quill实例,会导致内存泄漏(尤其是单页面应用,组件频繁切换时),在onUnmounted钩子处理:
<script setup>
import { onUnmounted, ref } from 'vue'
import { QuillEditor } from 'vue3-quill'
const quillInstance = ref(null)
const onEditorReady = (quill) => {
quillInstance.value = quill
}
onUnmounted(() => {
if (quillInstance.value) {
quillInstance.value.destroy() // 销毁Quill实例
quillInstance.value = null
}
})
</script>
集成Quill时那些“踩过的坑”咋解决?
实际开发中,样式乱、移动端适配差、粘贴内容脏这些问题很常见,分享四个高频坑的解决方案:
坑1:Quill样式和项目CSS冲突
Quill的snow主题自带样式,和项目全局CSS(比如Element Plus、Ant Design Vue)容易冲突,解决方法:
-
用深度选择器覆盖:Vue3中用
::v-deep(或>>>)修改Quill的样式,比如把工具栏按钮改成圆角:::v-deep .ql-toolbar .ql-button { border-radius: 4px; } -
自定义主题:如果冲突太多,直接复制Quill的默认样式文件,改成自己的类名,避免全局污染。
坑2:移动端工具栏按钮太小,点击难触发
手机上Quill的toolbar按钮默认尺寸小,用户点
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。