Quill富文本编辑器的实践 - DevUI
DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸
、灵活
、至简
的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠、取悦眼球的设计。如果你正在开发 ToB
的工具类产品
,DevUI 将是一个很不错的选择!
引言
富文本编辑器大概是最复杂、使用场景却极广的组件了。
可以说富文本编辑器让Web数据录入充满了无限的想象空间,如果只有文本框、下拉框这些纯文本的数据录入组件,那么Web的数据录入能力将极大地受限。我们将无法在网页上插入图片、视频这些富文本内容,更无法插入自定义的内容。
富文本编辑器让Web内容编辑变得更轻松、更高效,我们几乎可以在富文本编辑器中插入任何你想插入的内容,图片、视频、超链接、公式、代码块,都不在话下,甚至还可以插入表格、PPT、思维导图,甚至3D模型这种超复杂的自定义内容。
富文本编辑器的场景在Web上也是随处可见,写文章、写评论、意见反馈、录需求单,都需要使用到富文本。
本文结合DevUI团队在富文本组件中的实践,从使用场景、技术选型,再到对Quill的扩展,以及Quill的基本原理,跟大家分享Quill富文本编辑器的那些事儿。
本文主要由以下部分组成:
- 富文本编辑器的使用场景
- 技术选型
- 我们为什么选择Quill
- 如何扩展Quill
- Quill基本原理
以下内容来自Kagol
在华为 HWEB 大前端技术分享会
上的演讲。
富文本编辑器的使用场景
- 博客文章
- Wiki词条
- 工作项描述
- 测试用例步骤
- 反馈意见
- 评论
- …
技术选型
我们的需求:
- 开源协议友好
- Angular框架或框架无关
- 灵活可扩展
- 支持插入/编辑表格和图片
- 插件丰富,生态好
选型分析
- 首先排除官方不维护的
UEditor
- 然后排除React框架专属的
Draft.js
和Slate
- 接着排除开源协议不友好的
CKEditor
- 由于我们的业务场景丰富,需要富文本插入/编辑表格的功能,所以还需要排除不支持表格的
Trix
,弱支持表格的Etherpad
和Prosemirror
,以及表格功能收费的TinyMCE
- 最后只剩下
Quill
和wangEditor
两款编辑器可选,wangEditor
的扩展性和生态不如Quill
,所以最终选择Quill
作为富文本组件的基座
为什么选择Quill?
- BSD协议,商业友好
- 文档详细,上手快
- API驱动,扩展性好
- 插件丰富,生态好
文档详细
Document:https://quilljs.com/
介绍Quill的API:
介绍如何扩展Quill:
上手快
- 安装Quill:
npm i quill
- 引入样式:
@import 'quill/dist/quill.snow.css';
- 引入Quill:
import Quill from 'quill';
- 初始化Quill:
new Quill('#editor', { theme: 'snow' });
效果图:
API驱动,扩展性好
插件丰富,生态好
扩展Quill
插入标签
比如我想在编辑器里插入标签
上传附件
比如我想在编辑器里插入附件
插入表情
比如我想在编辑器中插入表情
类似语雀的评论:https://www.yuque.com/yuque/blog/sguhed
个性分割线
比如我想插入B站这种个性化的分割线
超链接卡片
比如我想插入知乎这样的超链接卡片
如何插入表情?
我们从如何插入表情入手,一起看看怎么在Quill中插入自定义的内容。
要在Quill中插入表情,只需要以下四步:
- 第一步:自定义工具栏按钮
- 第二步:自定义Blot内容EmojiBlot
- 第三步:在Quill注册EmojiBlot
- 第四步:调用Quill的API插入表情
第一步:自定义工具栏按钮
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
// 配置工具栏模块
toolbar: {
container: [ …, [ 'emoji' ] ], // 增加一个按钮
handlers: {
// 添加按钮的处理逻辑
emoji() {
console.log('插入表情');
}
}
},
}
});
给工具栏按钮增加图标
// 扩展Quill内置的icons配置
const icons = Quill.import('ui/icons');
icons.emoji = ‘<svg>…</svg>’; // 图标的svg可以从iconfont网站复制
效果如下:
工具栏上已经多了一个表情的按钮,并且能够响应鼠标点击事件,下一步就是要
编写插入表情的具体逻辑,这涉及到Quill的自定义内容相关的知识。
第二步:自定义Blot内容EmojiBlot
Quill中的Blot就是一个普通的ES6 Class,由于表情和图片的差别就在于:
Quill内置的图片格式不支持自定义宽高,而我们要插入的表情是需要特定的宽高的。
因此我们可以基于Quill内置的image格式来扩展。
emoji.ts
import Quill from 'quill';
const ImageBlot = Quill.import('formats/image');
// 扩展Quill内置的image格式
class EmojiBlot extends ImageBlot {
static blotName = 'emoji'; // 定义自定义Blot的名字(必须全局唯一)
static tagName = 'img'; // 自定义内容的标签名
// 创建自定义内容的DOM节点
static create(value): any {
const node = super.create(value);
node.setAttribute('src', ImageBlot.sanitize(value.url));
if (value.width !== undefined) {
node.setAttribute('width', value.width);
}
if (value.height !== undefined) {
node.setAttribute('height', value.height);
}
return node;
}
// 返回options数据
static value(node): any {
return {
url: node.getAttribute('src'),
width: node.getAttribute('width'),
height: node.getAttribute('height')
};
}
}
export default EmojiBlot;
第三步:在Quill注册EmojiBlot
有了EmojiBlot,要将其插入Quill编辑器中,还需要将这个ES6类注册到Quill中。
import EmojiBlot from './formats/emoji';
Quill.register('formats/emoji', EmojiBlot);
第四步:调用Quill的API插入表情
EmojiBlot注册到Quill中之后,Quill就能认识它了,也就可以调用Quill的API将其插入到编辑器中。
emoji(): void {
console.log(‘插入表情');
// 获取当前光标位置
const index = this.quill.getSelection().index;
// 在当前光标处插入emoji(blotName)
this.quill.insertEmbed(index, 'emoji', {
url: 'assets/emoji/good.png',
width: '64px',
});
},
效果图
Demo源码
源码链接:https://gitee.com/kagol/quill-demo
也欢迎关注我们DevUI组件库的官网,了解更多有趣又实用的开源组件!
DevUI官网:https://devui.design
Quill基本原理
最后讲一讲Quill的基本原理。
基本原理
- 使用Delta数据模型描述富文本内容及其变化,以保证行为的可预测
- 通过Parchment对DOM进行抽象,以保证平台一致性
- 通过Mutation Observe监听DOM节点的变化,将DOM的更改同步到Delta数据模型中
Quill如何表达编辑器内容?
Delta数据模型
通过Delta数据模型来描述富文本内容及其变化
Delta 是JSON的一个子集,只包含一个 ops 属性,它的值是一个对象数组,每个数组项代表对编辑器的一个操作(以编辑器初始状态为空为基准)。
{
"ops": [
{ "insert": "Hello " },
{ "insert": "World", "attributes": { "bold": true } },
{ "insert": "\n" }
]
}
修改编辑器内容
比如我们把加粗的"World"改成红色的文字"World",这个动作用 Delta 描述如下:
{
"ops": [
{ "retain": 6 },
{ "retain": 5, "attributes": { "color": "#ff0000" } }
]
}
意思是:保留编辑器最前面的6个字符,即保留"Hello "不动,保留之后的5个字符"World",并将这些字符设置为字体颜色为"#ff0000"。
删除编辑器内容
如果要删除"World"呢?
{
"ops": [
{ "retain": 6 },
{ "delete": 5 }
]
}
即:保留前面6个字符(’Hello ’),删除之后的5个字符(’World’)
Quill如何渲染内容?
渲染富文本内容的基本原理:
遍历Delta数组,将其中描述的内容一个一个应用(插入/格式化/删除)到编辑器中。
详情可参考DevUI专栏文章:
Quill如何扩展编辑器的能力?
扩展Quill的方式:
- 通过自定义Blot格式来扩展编辑器的内容
- 通过自定义模块来扩展编辑器的功能
详情可参考DevUI专栏文章:
THANK YOU!
Quill富文本编辑器的实践 - DevUI的更多相关文章
- Quill 富文本编辑器
Quill 富文本编辑器 https://quilljs.com/ https://github.com/quilljs/quill https://github.com/quilljs/awesom ...
- Vue整合Quill富文本编辑器
Quill介绍 Quill是一款开源的富文本编辑器,基于可扩展的架构设计,提供丰富的 API 进行定制.截止2021年1月,在github上面已有28.8k的star. Quill项目地址:https ...
- 轻量级quill富文本编辑器
因为公司产品需要在移动端编辑文本,所以发现了这个轻量级的好东西,网上也没找到比较好的案例,就自己总结了下,有兴趣的直接复制代码运行看看就知道啦! 下面是quill.js的CDN加速地址: <!- ...
- quill富文本编辑器 API
//1. 从第三个开始删除,删除4个 // console.log(this.quill.deleteText(2, 4)); // 12345678 1278 // 2.(返回对象)返回从第三个开始 ...
- Vue2 封装的 Quill 富文本编辑器组件 Vue-Quill-Editor
1.安装 npm install vue-quill-editor --save 2.使用 import { quillEditor } from 'vue-quill-editor' 3.组件中 & ...
- 现代富文本编辑器Quill的模块化机制
DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师.官方网站:devui.designNg组件库:ng-devui(欢迎S ...
- angular4 富文本编辑器
使用quill富文本编辑器实现,angular项目中用到了ngx-quill插件. quill的GitHub地址:https://github.com/quilljs/quill ngx-quill的 ...
- vue-quill-editor 富文本编辑器插件介绍
Iblog项目中博文的文本编辑器采用了vue-quill-editor插件,本文将简单介绍其使用方法. 引入配置 安装模块 npm install vue-quill-editor --save in ...
- 现代富文本编辑器Quill的内容渲染机制
DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师.官方网站:devui.designNg组件库:ng-devui(欢迎S ...
- Quill – 可以灵活自定义的开源的富文本编辑器
Quill 的建立是为了解决现有的所见即所得(WYSIWYG)的编辑器本身就是所见即所得(指不能再扩张)的问题.如果编辑器不正是你想要的方式,这是很难或不可能对其进行自定义以满足您的需求. Quill ...
随机推荐
- 超高并发下,Redis热点数据风险破解
★ Redis24篇集合 1 介绍 作者是互联网一线研发负责人,所在业务也是业内核心流量来源,经常参与 业务预定.积分竞拍.商品秒杀等工作. 近期参与多场新员工的面试工作,经常就 『超高并发场景下热点 ...
- 记录--iview 使用爬坑
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间公司需要开发一个后台管理系统,时间比较急迫,一两天时间.想一想自己一点一点的搭建起来可能性不太大,就想着有没有现成的可以改一改,就 ...
- TP6框架--CRMEB学习笔记:布置后台管理框架+配置路由
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近在研究一个基于TP6的框架CRMEB,这里分享下我的开发心得 首先在上篇文章中,我们安装了CRMEBphp接口项目,需要可以看这一篇 ...
- 使用docker运行nginx服务,挂载自定义配置文件
错误命令: 下面的方式,启动容器时,-d 后面跟一个指定容器ID的参数写在前面,导致容器不能正常启动,出现异常 docker run --name testnginx -d 7f0fd59e0094 ...
- vue中类tabs左右滑动
效果图 思路 给定一个变量用来记录滚动了几列,每滚动一次加1滚动一列,监听页面滚动父级元素宽度改变,重新设置滚动的距离(放在计算属性中让其自动计算) <template> <div ...
- ZYNQ7000系列学习
ZYNQ7000-系列知识学习 一.ZYNQ7000简介 ZYNQ7000是xilinx推出的具有ARM内核的FPGA芯片,可用于常见SOC开发.基于此,通过学习ZYNQ7000的各种设置和开发,可以 ...
- KingabseES 隐式游标属性值(SQL%attribute)
隐式游标介绍 Oracle数据库迁移到KingbaseES数据库,不需要将源PL/SQL脚本,大规模修改为KES语法,因为KingbaseES支持大部分PLSQL语法. 1.隐式游标 隐式游标是由 P ...
- 关于Dockerfile部署nginx,访问静态资源403Forbidden问题
今天项目遇到一个问题,服务器部署的nginx,在访问静态图片返回403 Forbidden. 容器是采用Dockerfile部署的,代码如下: FROM nginx:latest MAINTAINER ...
- ET介绍——强大的MongoBson库
强大的MongoBson库 后端开发,统计了一下大概有这些场景需要用到序列化: 对象通过序列化反序列化clone 服务端数据库存储数据,二进制 分布式服务端,多进程间的消息,二进制 后端日志,文本格式 ...
- #Splay#U137476 序列
题目 给定长度为\(n\)的序列\(Ai\) ,我们将按照如下操作给\(Ai\) 排序, 先找到编号最小的所在位置\(x1\) ,将\([1,x1]\) 翻转, 再找到编号第二小的所在位置\(x2\) ...