vue2 里的 quill 是什么?
做Vue2项目时,想给页面加个好用的富文本编辑器,很多人会想到Quill,但“vue quill vue2 怎么用”可不是一句话能说清的,从安装到自定义配置,再到解决实际开发里的坑,每一步都得踩稳,今天就把Vue2结合Quill的玩法拆碎了讲,不管是刚入门的新手,还是想优化富文本功能的老开发,都能找到能用的干货。
Quill本身是个现代的富文本编辑器,主打可扩展、API灵活,官网给的定位是“Powerful, modern WYSIWYG editor”,但Vue2项目不能直接用原生Quill,得靠**vue-quill-editor**这个封装库,它把Quill的核心能力适配成Vue组件,支持Vue2的响应式、组件化开发逻辑,让我们能通过Vue的语法(v-model`、`props`)去控制富文本编辑器的内容和配置。简单说,vue-quill-editor
是Vue2生态里对接Quill的“桥梁”,既保留了Quill本身的强大扩展性,又符合Vue2项目的开发习惯,比如你想做个带格式刷、自定义表情的编辑器,靠Quill的模块机制能实现,而vue-quill-editor
让这些功能在Vue组件里更好整合。
为什么在vue2项目选quill?
不是所有富文本场景都得选Quill,但它的优势在某些需求下特别“香”:
- 扩展性拉满:Quill的架构是基于“模块(Module) + 主题(Theme) + 插件(Plugin)”,比如想加个“一键生成目录”功能,或者自定义右键菜单,只要写对应的模块逻辑,就能嵌入到编辑器里,Vue2项目里通过
vue-quill-editor
的配置项,能很方便地把这些自定义模块挂上去。 - API 灵活到“贴脸”:Quill暴露了从文本内容到光标位置的细粒度API,比如在Vue组件里,你可以通过
ref
拿到Quill实例,然后调用quill.insertText()
插入指定文字,或者用quill.getSelection()
获取光标位置,实现“输入特定字符自动触发格式变化”这类定制逻辑。 - 社区资源够厚:这么多年下来,GitHub上围绕Quill的插件、主题一堆,比如
quill-table
支持表格编辑,quill-emoji
实现表情插入,在Vue2项目里,只要把这些插件和vue-quill-editor
结合好,能少写很多重复代码。
要是项目只需要“能输入文字、加个粗”这种极简需求,用textarea
或者更轻量的编辑器也够,但如果涉及复杂格式、自定义交互,Quill的扩展性就是降维打击。
怎么在vue2项目里装quill?
第一步得把依赖装对,因为要同时装Quill核心和Vue的封装库:
-
装包:打开终端,在Vue2项目根目录执行
npm install vue-quill-editor quill
(如果用yarn
就是yarn add vue-quill-editor quill
)
这里vue-quill-editor
是Vue2的封装层,quill
是原生编辑器核心,两者都得装。 -
全局注册(或局部引入)
想在整个项目里随时用<quill-editor>
组件,就去main.js
里全局注册:import Vue from 'vue' import QuillEditor from 'vue-quill-editor' // 引入Quill的默认样式,也可以自己改样式覆盖 import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' // snow是默认主题,还有bubble主题可选 import 'quill/dist/quill.bubble.css' Vue.use(QuillEditor)
要是只在某个组件里用,就局部引入:
import { quillEditor } from 'vue-quill-editor' import 'quill/dist/quill.snow.css' export default { components: { quillEditor } }
-
样式引入的坑
上面引入的是Quill官方默认样式,要是项目用了UI框架(比如Element UI),可能出现样式冲突(比如按钮样式被覆盖),这时候要么在全局样式里用!important
覆盖,要么自己仿写Quill的工具栏、编辑器区域样式,把冲突的CSS属性重新定义。
基础用法:写个能输入的富文本组件
先看最简示例,在Vue组件里用v-model
<template> <div> <!-- quillEditor 是组件名,对应局部或全局注册的 --> <quill-editor v-model="content" :options="editorOption" @blur="handleBlur" ></quill-editor> </div> </template> <script> import { quillEditor } from 'vue-quill-editor' import 'quill/dist/quill.snow.css' export default { components: { quillEditor }, data() { return { content: '<p>初始内容</p>', // 富文本内容,v-model双向绑定 editorOption: { theme: 'snow', // 使用snow主题,界面带工具栏 modules: { toolbar: [ ['bold', 'italic', 'underline'], // 加粗、斜体、下划线 [{ 'list': 'ordered' }, { 'list': 'unordered' }], // 有序、无序列表 [{ 'image': true }] // 图片上传按钮(默认是base64,后面讲怎么改) ] } } } }, methods: { handleBlur() { console.log('编辑器失去焦点,当前内容:', this.content) } } } </script>
这里有几个关键点:
v-model
绑定的content
,存的是富文本的HTML字符串,所以初始值可以是带标签的字符串,编辑器里修改内容会同步更新content
。editorOption
里的modules.toolbar
是配置工具栏按钮的,数组里每个元素对应一组功能,官方支持的按钮类型参考Quill官网,比如'bold'
对应加粗按钮,{ 'color': [] }
对应颜色选择器。- 事件处理:除了
@blur
,还能监听@change
变化时触发)、@focus
(获取焦点时)等,参数里能拿到编辑器实例和内容变化信息。
自定义配置:让quill更贴合业务
自定义工具栏按钮
默认工具栏按钮不够用?比如想加个“插入视频”或者“格式刷”按钮,以“插入自定义模板”为例:
在editorOption.modules.toolbar
里加自定义按钮:
editorOption: { modules: { toolbar: { container: [ ['bold'], // 自定义按钮组 [{ header: [1, 2, false] }], ['myCustomButton'] // 自定义按钮的标识 ], handlers: { // 给myCustomButton写点击逻辑 myCustomButton: function() { // this 指向Quill实例 const quill = this // 插入一段自定义HTML,比如带样式的文本 quill.insertHTML('<span style="color:red;">这是自定义内容</span>') quill.setSelection(quill.getLength()) // 把光标移到插入内容后面 } } } } }
还得给自定义按钮加样式(因为Quill默认没这个按钮的UI),可以在CSS里写:
.ql-toolbar .ql-buttons button.ql-myCustomButton { /* 自定义图标或文字,比如用背景图 */ background: url('./icon.png') no-repeat center; width: 24px; height: 24px; /* 隐藏默认文字(如果有的话) */ color: transparent; }
这样点击自定义按钮,就能插入预设内容,实现业务特有的交互。
替换图片上传逻辑(解决base64太大问题)
Quill默认把图片转成base64存到内容里,要是图片大,富文本内容会变得巨长,还影响性能,所以得改成“选图后上传到服务器,把服务器返回的图片URL插到编辑器里”。
步骤:
- 先把toolbar里的
image
按钮保留,但重写它的点击逻辑(或者拦截图片粘贴、拖拽的事件)。 - 在
editorOption
里配置图片处理逻辑:
editorOption: { modules: { toolbar: { container: [['image']], handlers: { image: function() { // 触发自己的文件选择框 const input = document.createElement('input') input.setAttribute('type', 'file') input.setAttribute('accept', 'image/*') input.onchange = () => { const file = input.files[0] if (file) { // 这里调自己的上传接口,比如uploadImg是封装的axios请求 this.$api.uploadImg(file).then(res => { const imgUrl = res.data.url // 获取当前光标位置,插入图片 const quill = this const range = quill.getSelection() if (range) { quill.insertEmbed(range.index, 'image', imgUrl) quill.setSelection(range.index + 1) // 光标移到图片后 } }) } } input.click() } } } } }
这样,点图片按钮时,会唤起系统文件选择框,选完图上传到服务器,再把服务器返回的URL插入编辑器,替代默认的base64逻辑。
自定义字体、字号
Quill默认字体选项有限,项目里要加“微软雅黑”“宋体”这类选项,得改配置:
editorOption: { modules: { toolbar: { container: [ [{ 'font': ['微软雅黑', '宋体', 'Arial'] }], // 字体选项 [{ 'size': ['small', false, 'large', 'huge'] }] // 字号 ] }, // 还得给Quill注册这些字体的样式,不然选了没效果 // 可以在全局CSS里写: /* .ql-font-微软雅黑 { font-family: "微软雅黑" !important; } .ql-font-宋体 { font-family: "宋体" !important; } */ } }
注意:字体名称要和CSS里的类名对应(ql-font-xxx
),所以配置font
选项时,数组里的名称要和CSS类名后缀一致,这样选字体时才能正确应用样式。
常见问题:这些坑怎么绕?
工具栏按钮样式乱了,和UI框架冲突
比如用Element UI时,Quill的按钮被改成Element的样式,导致图标显示异常,解决方法:
- 先排查是否引入了Quill的默认样式(snow或bubble),如果引入了,看是否被UI框架的全局样式覆盖。
- 给Quill的工具栏加独立的样式作用域,比如在外层包个class,然后用深度选择器(
>>>
或/deep/
)去覆盖:.my-quill-editor >>> .ql-toolbar .ql-button { /* 重新定义按钮样式,比如背景、边框 */ background: #fff; border: 1px solid #dcdcdc; }
图片上传成功,但编辑器里不显示图片
大概率是上传后拿到的URL不对,或者Quill的insertEmbed
调用有问题,检查:
- 服务器返回的URL是否是完整的可访问地址(比如带
http/https
)。 quill.getSelection()
是否拿到有效的range
(比如用户没选任何位置时,range
可能是null
,这时候要处理默认插入位置)。
可以加个兜底逻辑:
if (range) { quill.insertEmbed(range.index, 'image', imgUrl) } else { quill.insertEmbed(0, 'image', imgUrl) // 没选位置就插在开头 }
移动端(手机、平板)点击工具栏没反应
Quill的默认事件处理在移动端可能有兼容问题,尤其是自定义按钮,可以检查:
- 是否给按钮加了
touchstart
事件支持(但Quill本身对移动端有基础支持,可能是自己写的handlers
里没处理touch事件)。 - 用Chrome的手机模拟模式测试,看控制台是否有事件绑定错误。
- 有些情况下,给编辑器外层加
user-select: auto
(默认Quill可能设置了user-select: none
影响交互)。
v-model
回显不对
比如从接口拿到的富文本HTML,在编辑器里显示成纯文本,原因是Quill的内容需要是合法的Delta格式或者HTML,但有时候接口返回的HTML里标签被转义了(比如<p>
变成&lt;p&gt;
),解决:
- 确保接口返回的HTML是未转义的,或者在赋值给
content
前,用DOMParser
转义回来。 - 初始化时,给
content
赋值前,先检查是否是正确的HTML字符串,必要时用this.content = this.content.replace(/&/g, '&')
这类操作还原。
进阶场景:把quill玩出花
结合表单提交 作为表单的一部分,和其他表单项一起提交,比如用Element UI的Form组件:
<el-form :model="form" label-width="100px"> <el-form-item label="标题"> <el-input v-model="form.title"></el-input> </el-form-item> <el-form-item label="内容"> <quill-editor v-model="form.content" :options="editorOption"></quill-editor> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> </el-form-item> </el-form> <script> export default { data() { return { form: { title: '', content: '' }, editorOption: { /* 配置 */ } } }, methods: { submitForm() { // 这里调接口,把form对象发出去 this.$api.submitArticle(this.form).then(res => { if (res.code === 200) { this.$message.success('提交成功') } }) } } } </script>
注意:富文本内容可能包含大量HTML,接口要支持接收大文本,必要时调整后端接口的请求体大小限制。
实现undo/redo(撤销/重做)
Quill本身内置了历史记录模块,只要在配置里开启:
editorOption: { modules: { history: { delay: 1000, // 记录间隔,毫秒 maxStack: 100, // 最大记录数 userOnly: false // 是否只记录用户操作(默认false,自动保存的操作也记录) } } }
然后通过Quill实例调用quill.history.undo()
和quill.history.redo()
,可以在Vue组件里加两个按钮,绑定方法:
<template> <div> <quill-editor ref="myQuill" v-model="content" :options="editorOption"></quill-editor> <el-button @click="handleUndo">撤销</el-button> <el-button @click="handleRedo">重做</el-button> </div> </template> <script> export default { methods: { handleUndo() { const quill = this.$refs.myQuill.quill // 获取Quill实例 quill.history.undo() }, handleRedo() { const quill = this.$refs.myQuill.quill quill.history.redo() } } } </script>
权限控制:不同角色看到不同工具栏
比如普通用户只能用加粗、列表,管理员能上传图片、改字体,可以通过动态渲染editorOption.modules.toolbar.container
实现:
data() { return { userRole: 'admin', // 从接口或全局状态拿角色 editorOption: { modules: { toolbar: { container: [] } } } } }, mounted() { // 根据角色生成toolbar配置 let toolbarConfig = [] if (this.userRole === 'admin') { toolbarConfig = [ ['bold', 'italic'], [{ 'image': true }], [{ 'font': ['微软雅黑'] }]
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。