Rust+React创建富文本编辑器

目录
  • 简介
  • 数据模型
  • 核心逻辑
  • 视图
  • 手动差异化
  • 杂项
  • 总结

简介

在Fiberplane,我们最近遇到了一个有趣的挑战:我们正在使用的富文本编辑器库已经过时了。我们曾经使用Slate.js——一个很好的编辑器——但是当我们为协作编辑实现我们自己的富文本基元时,我们发现我们自己的基元和Slate的数据模型之间的脱节是一个阻碍因素。所以我们开始思考——如果我们建立自己的富文本编辑器(RTE, Rich Text Editor)会怎样?

从一个非常高层次的角度来看,一个富文本编辑器是由两个部分组成的。

  • 一个数据模型和对其进行操作的核心逻辑。
  • 一个渲染上述数据模型的状态并处理用户互动的视图。

我们在视图中使用了Slate,但结果是它也拉入了自己的数据模型。如果我们可以直接在React中实现视图,我们可以大大简化我们的堆栈,并完全控制它的每个方面。缺点是什么?RTEs因为需要支持复杂的用户交互而臭名昭著,而现在我们需要自己处理每一个交互。

在这篇文章中,我们将讨论我们所面临的挑战以及我们如何解决这些问题。

数据模型

我们的产品是一个协作式的笔记本编辑器。笔记本是一个基于块的编辑器,由不同类型的单元组成,从文本单元到图片和图表。因此,我们确定了一个数据模型,它既有利于我们的协作功能,也有利于为我们在单元格内使用的任何富文本字段提供动力的RTE。在这篇文章中,我们将重点讨论TextCell

struct TextCell {
    pub id: String,
    pub content: String,
    pub formatting: Option<Formatting>,
}

这里的content只是纯文本内容,而formatting是将纯文本变成富文本的东西。"多汁"的部分都在格式化类型里面。

type Formatting = Vec<AnnotationWithOffset>;
​
struct AnnotationWithOffset {
    annotation: Annotation,
    offset: u32,
}
​
enum Annotation {
    StartBold,
    EndBold,
    StartItalics,
    EndItalics,
    StartLink { url: String },
    EndLink,
    /* more like these... */
}

正如你所看到的,这只不过是一个注释列表,它定义了要应用的格式化类型和它开始的偏移量。我们有意不选择类似于HTML的树状结构,因为格式化范围可以重叠,这将导致复杂的树状操作。此外,每个注释只有一个偏移量的简单性使我们很容易实现我们用于协作的操作转换(OT)算法。

核心逻辑

随着数据模型的出现,也带来了与之互动的代码。当你在一个单元格中打字时,我们在哪里插入新打的字符?这如何影响content和相关的formatting?如果你在一个选择上切换格式,应该发生什么?如果你将一个单元格从中间分割开来,又该怎么办?所有这些以及更多都在Rust的核心逻辑中实现。

你要知道,无论如何我们都需要这些逻辑,因为我们的OT算法也需要它。但现在我们也能用同样的原语来驱动我们的编辑器。

为了使这个逻辑易于测试,它被实现为纯函数,我们在TypeScript的Redux reducer中调用。我们创建了fp-bindgen来生成Rust代码和调用它的TypeScript代码之间的绑定关系。

为了适应RTE(当我们还在使用Slate时还不需要),我们不得不自己引入一段逻辑,就是光标管理。例如,当用户按下左方向键时,我们分派一个MoveCursor动作,其有效载荷如下。

struct MoveCursorPayload {
    pub delta: i32,
    pub extend_selection: bool,
    pub unit: CursorUnit,
}

delta指定光标是向前还是向后移动,通过指定一个1-1的值。extend_selection属性是在用户按住Shift键时使用的,用来扩展当前的选择,或者在还没有选择的情况下创建一个。这个unit决定了我们是按Unicode字母群(用户通常称之为 "字符")还是按单词移动光标,用于用户按住Ctrl/键时。然后,我们的Rust还原器会处理这些动作,并处理所有的边缘情况,包括确保光标不会出现在@的中间。

视图

在我们RTE的大部分开发过程中,我们的编辑器甚至不是一个编辑器。至少从浏览器的角度来看不是。这是因为浏览器通常只识别两种类型的编辑器:纯文本编辑器,如<input><textarea>元素,以及使用一种叫做contenteditable的属性创建的自由格式编辑器。我们的编辑器两者都不是。

我们在最终版本中仍然使用contenteditable属性,因为我们很快会讨论一些实际的影响,但我们有意识地决定尽可能少地依赖它。这对我们最初构建RTE的方式产生了深远的影响,你将在本节中看到。

如果我们最初的版本根本没有使用contenteditable,那么我们怎么能够创建一个富文本编辑器?从用户的角度来看,RTE只不过是一个看起来像文本字段的东西,有一个光标,允许他们输入任何他们喜欢的内容。

所以我们创建了一个普通的React组件,并根据单元格的contentformatting生成了富文本内容,然后使用React.createElement()插入实际的元素,这些元素只是一个应用了样式的<span>元素的平面列表(偶尔会有<a>元素洒在链接上)。然后,我们添加了必要的事件处理程序来捕捉用户的互动,这又将再次调用数据模型上的适当逻辑。

那么用户的光标呢?只是另一个我们自己插入的小React组件。我们会在useLayoutEffect()钩子中测量它需要的位置,然后根据这个来定位它。

所以......很简单,很容易,对吗?好吧,我们现在需要处理的大量的交互使这成为一个重大的挑战。例如,让我们再看一下光标导航。上一节中的例子显示了如何向左和向右移动光标。但是如果用户按了向下的箭头,他们的光标最终会在哪两个字符之间呢?这不是一个简单的问题,因为保持光标的垂直位置需要测量上面那一行的字符的位置。但你如何定义什么是 "上面那一行"?无论是content还是formatting都不包含这些信息。然后记住我们还必须支持选择。还有鼠标互动...

这当然会让人感到不知所措,在开发过程中,可能很难保持对哪些工作和哪些不工作的概述。而这正是我们觉得最初没有contenteditable的工作很好的原因。我们自己做所有的事情,使我们非常清楚自己的位置。任何不工作的交互都是我们仍然需要实现的。没有什么会意外地工作,因为浏览器为我们解决了这个问题--浏览器在这里处于次要地位。

当然,对于最终的版本,很难绕过使用contenteditable。这是因为如果没有它,浏览器扩展将无法识别你的编辑器。而移动浏览器甚至会顽固地拒绝调出屏幕键盘......

手动差异化

所以我们确实需要contenteditable,但是还有一个问题。React不支持对已启用contenteditable的元素的内容进行修补。这是有原因的:contenteditable基本上是告诉浏览器去玩吧。这就像一个没有规则的操场。

React并不喜欢这样。它依靠虚拟DOM来决定它需要如何更新实际的DOM,但当浏览器可以在它不知情的情况下把地毯从它下面拉出来并更新实际的DOM时,这种方法就陷入了困境。这也是我们一开始就避免的原因。为了在更新我们的数据模型时能够保留用户的意图(OT算法的一个重要方面),最好是了解导致任何变化的互动。但是,如果你试图理解浏览器对DOM在内容可编辑元素中的变化,你最多只能是猜测。

所以我们借鉴了React的玩法,实现我们自己的差异算法。但我们不是针对虚拟DOM进行差分,而是在useLayoutEffect()钩子函数中针对真实DOM进行差分和修补。这相对简单,因为我们的用例非常专业,而且它还有一个好处,如果真实DOM中发生任何意外(可能是由于浏览器扩展),我们的算法将简单地将视图恢复到我们基于数据模型的预期。

杂项

上述所有内容可能会让你对编辑器的工作原理有一个较高的认识,但魔鬼是在细节中的。下面是我们需要解决的一些小问题。

  • 支持Unicode。每个人都喜欢的标准,但在工作中却很麻烦。幸运的是,Rust有优秀的unicode_segmentation板块,对我们帮助很大。这帮助我们解决了一些问题,比如按字进行光标导航,以及确保光标能正确地跳过字母群。
  • 光标定位是很棘手的,但我们发现最好的方法是使用浏览器的Selection对象,并通过这种方式设置一个(透明的)本地光标。然后我们使用getBoundingClientRect()来测量浏览器渲染光标的位置,然后我们就可以在那里定位我们自己的光标。
  • 组合事件被浏览器用来组成带有重音的字符和处理拼音等输入。不要忘记处理这些。

总结

创建你自己的富文本编辑器是一项艰巨的任务,但只要有正确的架构和良好的规划,它肯定是可以做到的。如果你发现自己处于必须选择或开发一个富文本编辑器的位置,我们希望你能发现这篇文章的有用信息。

注:特别感谢技术指导dazhao(赵达)对本文翻译的审阅指正。

作者:Arend van Beelen

原文链接:Creating a Rich Text Editor using Rust and React

以上就是Rust+React创建富文本编辑器的详细内容,更多关于Rust React富文本编辑器的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue集成一个支持图片缩放拖拽的富文本编辑器

    需求: 根据业务要求,需要能够上传图片,且上传的图片能在移动端中占满屏幕宽度,故需要能等比缩放上传的图片,还需要能拖拽.缩放.改变图片大小.尝试多个第三方富文本编辑器,很难找到一个完美符合自己要求的编辑器.经过多次尝试,最终选择了wangEditor富文本编辑器. 最初使用的是vue2Editor富文本编辑器,vue2Editor本身是不支持图片拖拽的,但是提供了可配置图片拖拽的方法,需要借助Quill.js来实现图片拖拽.虽然满足了业务需求,但是在移动端展示的效果不是很理想. 此次编辑器主要是

  • Vue+Element UI+vue-quill-editor富文本编辑器及插入图片自定义

    本文为大家分享了Vue+Element UI+vue-quill-editor富文本编辑器及插入图片自定义,供大家参考,具体内容如下 1.安装 npm install vue-quill-editor --save 2.在main.js中引入 import VueQuillEditor from 'vue-quill-editor' import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quil

  • js获取UEditor富文本编辑器中的图片地址

    写之前在网上找了很多方法,最简单的思路应该是1.获取UEditor中的内容:2.将获取到的字符串转换成jquery对象:3.选择器找到img元素,获取src值. var content= UE.getEditor('details').getContent();//获取编辑器内容 var $div = document.createElement("div");//创建一个div元素对象 $div.innerHTML = content;//往div里填充html var $v = $

  • vue使用vue-quill-editor富文本编辑器且将图片上传到服务器的功能

    一.准备工作 下载vue-quill-editor npm install vue-quill-editor --save 或者 yarn add vue-quill-editor 二.定义全局组件quill-editor 下载好vue-quill-editor后,我们需要定义一个全局组件,把这个组件名字命名为quill-editor 1.定义template模板 <div> <quill-editor v-model="value" ref="myQuil

  • vue-froala-wysiwyg 富文本编辑器功能

    近期需要在vue3项目上做一个富文本编辑器,找了很多插件组件,最终决定用 froala.虽然不是免费的,但是功能实在是太强大了. froala 文档:https://www.froala.com/wysiwyg-editor/docs/overview froala 官方demo: https://www.froala.com/wysiwyg-editor/examples 下面介绍在vue3.中如何安装使用froala. Step1: froala 依赖于jQuery.所以要安装jQuery:

  • 如何在vue中使用kindeditor富文本编辑器

    第一步,下载依赖 yarn add kindeditor 第二步,建立kindeditor.vue组件 <template> <div class="kindeditor"> <textarea :id="id" name="content" v-model="outContent"></textarea> </div> </template> <s

  • Vue使用富文本编辑器Vue-Quill-Editor(含图片自定义上传服务、清除复制粘贴样式等)

    使用教程(注意细看总结部分,写了几点,希望有所帮助): 1.安装插件:npm install vue-quill-editor 2.安装插件依赖:npm install quill 3.main.js文件中引入: import Vue from 'vue' import VueQuillEditor from 'vue-quill-editor' import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' impor

  • Rust+React创建富文本编辑器

    目录 简介 数据模型 核心逻辑 视图 手动差异化 杂项 总结 简介 在Fiberplane,我们最近遇到了一个有趣的挑战:我们正在使用的富文本编辑器库已经过时了.我们曾经使用Slate.js——一个很好的编辑器——但是当我们为协作编辑实现我们自己的富文本基元时,我们发现我们自己的基元和Slate的数据模型之间的脱节是一个阻碍因素.所以我们开始思考——如果我们建立自己的富文本编辑器(RTE, Rich Text Editor)会怎样? 从一个非常高层次的角度来看,一个富文本编辑器是由两个部分组成的

  • layui富文本编辑器前端无法取值的解决方法

    首先,需要简单说下layui这个框架,个人觉得属于那种比较好用的框架,包括他的极简流畅的弹框,很适合那种主要写后端的程序员和快速开发的团队 本期主要讲的是layui富文本编辑器这个东西,他的模式是在某个textarea的基础上建立了一个富文本编辑器,也就是说你填写在富文本编辑器的值其实是放在编辑器里面的,并没有放在之前的textarea中,你获取textarea的值的时候需要先把富文本编辑器的值同步到之前的textarea中,然后才能通过textarea获取对应的值 主要代码如下: 下图是通过浏

  • layui 富文本编辑器和textarea值的相互传递方法

    1.富文本编辑器传递值给textarea <div class="layui-form-item layui-form-text"> <label class="layui-form-label">资讯内容</label> <div class="layui-input-block"> <textarea name="content" id="demo"

  • Vue.js结合Ueditor富文本编辑器的实例代码

    在前端开发的项目中.难免会遇到需要在页面上集成一个富文本编辑器. 前一段时间公司Vue.js项目需要使用UEditor富文本编辑器,在百度上搜索一圈没有发现详细的说明,决定自己尝试,忙活了一天终于搞定了. 1. 总体思路 1.1 模块化 vue的很大的一个优势在于模块化,我们可以通过模块化实现页面和逻辑的复用.所以可以把Ueditor重新封装成一个.vue的模板文件.其他组件通过引入这个模板实现代码复用. 1.2 数据传输 首先父组件需要设置编辑器的长度.宽度.初始文本,这些数据可以通过prop

  • nodejs后台集成ueditor富文本编辑器的实例

    UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码.. 1 下载ueditor nodejs版本 2 复制public目录下面的文件 到项目静态资源public文件夹下 3 在项目根目录创建ueditor文件夹 要复制进来的内容为 4 在根目录的 ueditor文件夹下执行 npm install 安装此目录下面package.json依赖的模块 5 项目根目录下创建 ue.js 代码部分来自于

  • 不到200行 JavaScript 代码实现富文本编辑器的方法

    前段时间在寻找一些关于富文本编辑器的资料,然后发现了这个名为 Pell 的项目,它是一个所见即所得(WYSIWYG)的文本编辑器,虽然它的功能很简单,但是令人吃惊的是它只有 1kb 大小.而项目最核心的文件 pell.js 只有130行,即使加上其它部分,总的 js 数量也不到200行.这引起了我的兴趣,决定看看它的源码是如何做到这一点的. 项目的主要代码在 pell.js文件中,其结构很简单,主要功能的实现依赖于以下的几个部分 actions 对象 exec() 函数 init() 函数 Do

  • vue中使用ueditor富文本编辑器

    最近在做后台管理系统的时候遇到要使用富文本编辑器.最后选择了ueditor,我的项目使用 vue+vuex+vue-router+webpack+elementUI的方案完成框架的搭建, 1.下载UEditor官网最新的jsp版本的包,下载完成解压之后得到一个utf8-jsp的文件夹,里面包含的内容如下: 2.将这个文件夹改名为ueditor,并且移入自己项目中的static文件夹下,修改ueditor.config.js文件夹中的内容,如下图: 3.编写子组件 <template> <

  • Vue2.0中集成UEditor富文本编辑器的方法

    在vue的'项目中遇到了需要使用富文本编辑器的需求,在github上看了很多vue封装的editor插件,很多对图片上传和视频上传的支持并不是很好,最终还是决定使用UEditor. 这类的文章网上有很多,我进行了摸索.手写代码.汇总.排版,形成了这篇文章. 下载对应的UEditor源码 首先,去官网上下载UEditor的源码,根据你后台语言的不同下载对应的版本(PHP.Asp..Net.Jsp). http://ueditor.baidu.com/website/download.html 下载

  • 轻量级富文本编辑器wangEditor结合vue使用方法示例

    1.安装 使用npm下载: `npm install wangeditor` 2. 创建实例 (1)基本用法: <template> <div> <div id="editor" class="editor"></div> </div> </template> <script> import E from 'wangeditor' export default { name: 'ed

  • PHP如何搭建百度Ueditor富文本编辑器

    本文为大家分享了PHP搭建百度Ueditor富文本编辑器的方法,供大家参考,具体内容如下 下载UEditor 官网:下载地址 将下载好的文件解压到thinkphp项目中,本文是解压到PUBLIC目录下并改文件夹名称为ueditor 第一步 引入javascript 在html中如入下面的js语句引入相关文件 <script type="text/javascript" charset="utf-8" src="__PUBLIC__/ueditor/u

随机推荐