Yjs + quill:快速实现支持协同编辑的富文本编辑器
大家好,我是前端西瓜哥,这次来看看 Yjs 如何帮助我们实现协同编辑能力的。
Y.js 是一个支持 协同编辑 的开源库。只要我们将自己的数据转换为 Y.js 提供的 Y.Array
、Y.Map
类型,Y.js 就会自动帮我们做数据的一致性处理和同步。
一致性问题
协同编辑一个很棘手的问题是:多个用户同时编辑产生的冲突要怎么处理,如何保证一致性?
比如两个用户同时往一个文本的末尾加上不同的字符,最终谁的字符在前,谁的字符在后?
目前业界有两种方案,一个是 OT (Operational transformation)算法,是比较主流的一种解法。流行的开源解决方案是 ShareDB。
它的核心在于 Transform
(转换):服务端接收两个客户端的对同一版本数据的原子操作行为,转换出它们各自要做的不同操作,然后传递给各个客户端并应用,最终让它们的内容是一致的。
我之前写过一篇介绍 OT 算法的文章,讲的会更详细一些,可以去看看:
《协同编辑中使用的 OT 算法是什么?》
另一种是 CRDT(Conflict-free Replicated Data Type),中文就是 “无冲突复制数据类型”,主要被应用在分布式系统中,即可以不需要中心化服务器。流行的开源方案是 Yjs。
但 CRDT 需要传输更多的数据,有不小的内存和性能开销,且相比 OT 被提出地更晚,学术研究相对较少,所以一开始算不上是主流。
然而随着 Yjs 的出现并做了不少性能优化,CRDT 方案也逐渐流行了起来,越来越多新的协同工具选择使用 Yjs 来作为数据一致性的解决方案。
Yjs 是基于操作的 CRDT,其原理简单来说,就是记录所有用户的操作,这些操作会拼接到一个双向链表中,并通过通用的算法保证确定的顺序,最后所有客户端都能得到相同的一条链表,最后得到的数据自然也是一致的。
Yjs + Quill:打造协同工具
我们来写个 demo 感受一下 Yjs 的强大之处。
先用 vite 搭个普通的不带框架的脚手架,这里我用的 pnpm,其他包管理工具也行。
pnpm create vite
项目名为 yjs-quill-demo
,选择 Vanilla(不用框架的意思),然后选择 JavaScript(如果你熟悉 TS,也可以选 TS)
接着是进入文件夹,安装依赖,并运行。
cd yjs-quill-demo
pnpm install
pnpm run dev
打开浏览器输入控制台输出的链接,可以看到:
下面我们来安装依赖。
首先是开源编辑器 quill 和它的插件 quill-cursors。这个插件可以展示一些其他用户的光标的状态
pnpm add quill quill-cursors
将 mian.js 文件原来的内容删除,加上下面内容:
import Quill from 'quill';
import QuillCursors from 'quill-cursors';
import 'quill/dist/quill.snow.css'; // 使用了 snow 主题色// 使用 cursors 插件
Quill.register('modules/cursors', QuillCursors);const quill = new Quill(document.querySelector('#app'), {modules: {cursors: true,toolbar: [[{ header: [1, 2, false] }],['bold', 'italic', 'underline'],['image', 'code-block'],],history: {userOnly: true, // 用户自己实现历史记录},},placeholder: '前端西瓜哥...',theme: 'snow',
});
效果:
下面我们就要引入 Yjs,给 quill 加上协同编辑功能。
Yjs 官方提供了 y-quill 库,通过它可以将 quill 数据模型和 Yjs 数据模型进行绑定。
pnpm add yjs y-quill
追加 Yjs 相关逻辑:
import * as Y from 'yjs';
import { QuillBinding } from 'y-quill';
// ...const ydoc = new Y.Doc(); // y 文档对象,保存需要共享的数据
const ytext = ydoc.getText('quill'); // 创建名为 quill 的 Text 对象
const binding = new QuillBinding(ytext, quill); // 数据模型绑定
ok,接下来就是要接上服务端,实现数据传输了。服务的提供者,Yjs 称为 provider,大概可以翻译为 “供应者” 的意思。
Yjs 官方提供了几种 Provider:WebRTC、WebSocket、Dat。
这里我们用比较常见的 WebSocket。
pnpm add y-websocket
代码:
import { WebsocketProvider } from 'y-websocket';
// ...// 连接到 websocket 服务端
const provider = new WebsocketProvider('wss://demos.yjs.dev', 'quill-demo-room', ydoc);
// 数据模型绑定,再额外绑上了光标对象
const binding = new QuillBinding(ytext, quill, provider.awareness);
这里的服务器用的是 Yjs 提供的 demo 体验用的服务器,因为一些喜闻乐见的原因,可能会连不上这个服务器。
然后你会发现,如果在同一浏览器打开两个 tab,没连上服务也能做协同编辑。这是因为 Yjs 会优先通过浏览器的同 host 共享状态的方式进行通信,然后才是网络通信。所以最好是打开两个不同的浏览器做调试。
我们验证一下。
左边两个 tab 页来自同一个浏览器,右边则是另一个浏览器。
当修改被我限速为 1 KB/s 的 tab 的编辑器内容时,来自同一浏览器的另一个 tab 页立刻发生了变更(证明通信走的是本地),而另一个浏览器的 tab 则慢得多(说明走的网络通讯)。
我们也可以自己在本地起一个服务器,做法是:
HOST=localhost PORT=1234 npx y-websocket
对应着要改一下客户端代码中 ws 服务的地址:
const provider = new WebsocketProvider('ws://localhost:1234', 'quill-demo-room', ydoc);
完整代码
import Quill from 'quill';
import QuillCursors from 'quill-cursors';
import 'quill/dist/quill.snow.css'; // 使用了 snow 主题色
import * as Y from 'yjs';
import { QuillBinding } from 'y-quill';
import { WebsocketProvider } from 'y-websocket';// 使用 cursors 插件
Quill.register('modules/cursors', QuillCursors);const quill = new Quill(document.querySelector('#app'), {modules: {cursors: true,toolbar: [[{ header: [1, 2, false] }],['bold', 'italic', 'underline'],['image', 'code-block'],],history: {userOnly: true, // 用户自己实现历史记录},},placeholder: '前端西瓜哥...',theme: 'snow',
});const ydoc = new Y.Doc(); // y 文档对象,保存需要共享的数据
const ytext = ydoc.getText('quill'); // 创建名为 quill 的 Text 对象
// 连接到 websocket 服务端
const provider = new WebsocketProvider('wss://demos.yjs.dev', 'quill-demo-room', ydoc);
// 数据模型绑定,再绑上光标对象
const binding = new QuillBinding(ytext, quill, provider.awareness);
结尾
因为用了很多 Yjs 提供的模块化的包,其实我们并没有接触到太多的实现细节,尤其是将数据绑定到 Yjs 提供的类型数据的实现。只能说是简单体验了 Yjs 配合 quill 实现协同编辑的效果。
我是前端西瓜哥,欢迎关注我,学习更多前端知识。
Yjs + quill:快速实现支持协同编辑的富文本编辑器相关推荐
- Quill – 可以灵活自定义的开源的富文本编辑器
Quill 的建立是为了解决现有的所见即所得(WYSIWYG)的编辑器本身就是所见即所得(指不能再扩张)的问题.如果编辑器不正是你想要的方式,这是很难或不可能对其进行自定义以满足您的需求. Quill ...
- 支持粘贴图片的富文本编辑器
公司做的项目需要用到粘贴Word功能.就是将word内容一键粘贴到网页编辑器(在线富文本编辑器)中.Chrome+IE默认支持粘贴剪切板中的图片,但是我要粘贴的文章存在word里面,图片多达数十张,我 ...
- summernote 编辑html,富文本编辑器summernote使用
先看看summernote是个什么东西! 它有什么好处? 1.界面优雅:基于bootstrap的样式,个人感觉比ckeditor要好看: 2.插入图片方便:可以通过Ctrl+V的方式插入粘贴板中的图片 ...
- flex html 控制编辑,在富文本编辑器中添加Tab键功能(Flex 3)
我试图在使用事件keyDown的富文本编辑器中添加Tab键功能.只需要清楚,在标签键上,我需要将控件保留在前面带有'\ t'空格的同一个文本框中,而不是跳到下一个对象.我尝试的下面的代码完全适用于te ...
- ckeditor5html编辑,CKEditor5富文本编辑器如何取值
ClassicEditor .create( document.querySelector( '#editor' ), { //工具栏,可选择添加或去除 toolbar: ['headings', ' ...
- 现代化富文本编辑器 Quill Editor
介绍 近期在弄富文本编辑器相关的内容,其中项目中使用了 Quill Editor(后面简称 Quill).Quill 自称是一个现代化强大的富文本编辑器,它与其它富文本编辑器(例如 UEditor)不 ...
- 前端Quill实现富文本编辑器
目录 前言 Quill 介绍 Quill 简单实现 效果 前言 网页上经常会需要用户输入一些文本或者代码块,所以需要富文本编辑器来支持.常见的富文本编辑器有Quill.Draft.js.TinyMCE ...
- django快速集成富文本编辑器wangeditor
django快速集成富文本编辑器wangeditor django是python栈一款优秀的web开发框架,也是python栈web开发框架中使用占比最高的开发框架,至于其是否足够优秀不言自明,自带的 ...
- vue中使用quill富文本编辑器
因为公司的需求,对比了线在很火的几款富文本编辑器,最终选定了quill,他够轻量,拓展性也比较高,除了文档是英文的阅读不方便之外,都很适合公司项目.故整理出来,也方便以后使用. 在网上找了一个中文文档 ...
最新文章
- 【转】【C#】判断两个文件是否相同
- Python自动化之语法基础
- 提高虚拟机运行速度方法
- Flink 作为现代数据仓库的统一引擎:Hive 集成生产就绪!
- Phonegap在ios7上系统状态栏的问题解决
- 读书笔记--Head First C#目录
- iOS中内存管理的问题——堆和栈
- 现在论文用手写还是用计算机写,毕业论文计算机手写数字识别技术完整版.docx...
- 给模型自动赋予贴图代码
- 心理账户、沉没成本、比例偏见
- iReport与JasperReport中如何支持文支持显示
- 在XP上同时运行IE6,IE7,IE8,IE9
- 谷歌团队在平安金融中心_Google银行业务可以教给我们关于金融和科技的未来
- 使用EChat通过php读取后台数据将其转换为饼状图
- 交换机开发(一)—— 交换机的工作原理
- HTML5期末大作业:动漫网站设计——斗破苍穹动漫(6页) HTML+CSS+JavaScript 学生动漫网页设计模板下载 斗破大学生HTML网页制作作品 简单漫画网页设计成品 dreamweav
- 全球化观点的生产模式
- 网安之php开发第十三天
- (附源码)ssm养老院信息管理系统 毕业设计211141
- 《QT+PCL第七章》点云分割-CPC