什么是富文本?

富文本 Rich Text ,相关于纯文本 Plain Text 来说,便是有通用的格局选项(比方加粗和斜体)来格局化的文本。

Web 富文本编辑器演进

Web + 富文本修正器

Web 富文本编辑器演进

众所周知,Web 是现在来说最通用的渠道,不管是电脑、手机、游戏机、轿车仍是 Kindle ,只需有浏览器就能打开网站。加上 WebAssembly 和 Electron 等技能和东西的支撑,在 Web 上执行杂乱操作的超级运用(Figma, VS Code)也在连续上台。面临 “一次编写,随处运转” 的引诱下,许多传统桌面运用也都在逐渐地将阵地拓宽到 Web 端,富文本修正器便是其中之一。

富文本修正器 = 坑?

在 Web 前端业界内,富文本修正器是公认的天坑。Web 开发长处是跨渠道,但问题也是因为跨渠道带来的兼容问题。跨渠道仅仅能在各个渠道上跑起来,可是跑起来怎么样便是天坑地点了。

在 Web 开发修正器首先要处理好 焦点光标选区撤回栈、从外部张贴内容解析等等的坑,然后考虑兼容不同浏览器(Chrome、Firefox、Safari 等)…… 处理好了最根本的英文输入后,以中文运用者为代表的用户来要求支撑 IME 组合输入了…… 解决了 IME 输入后,RTL 语言(希伯来语、阿拉伯语)用户来了…… 手机用户来了…… 协同修正用户来了……

借用知乎上的一段话,总结起来便是:

落后的生产力与人们日益增长的需求之间的矛盾。

落后生产力:

  1. Web 相关规范推进缓慢
  2. 浏览器厂商关于相同操作或者场景完成方式的不同,导致兼容性的问题
  3. 运用 HTML DOM 描绘富文本内容有太多不行操控的状况

日益增长的需求:

  1. 不确定的交互目的,比方按 Delete 键,不同的焦点位置有不同的状况需要考虑
  2. 内容输入的多样性,比方有:打字键入、张贴、拖拽等,每个处理起来都适当杂乱
  3. 大量需要拦截阻止和署理的浏览器默许行为,确保数据的完整性和正确性
  4. 用户关于修正器的运用要求越来越高,比方:兼并单元格、列表多级嵌套、协同修正、版本对比、段落标注,咱们都以为这是根本需求,其实这儿面的技能难度是超出咱们的想象的。

修正器技能阶段一览

阶段 描绘 典型产品
L0
  1. 强依靠浏览器 DOM API ( contenteditable, document.execCommand )
  2. 视图即数据
  1. UEditor
  2. TinyMCE
  3. CKEditor 1 ~ 4
L1
  1. 依然根据 contenteditable
  2. 扔掉 document.execCommand 操作内容,改为自己完成
  3. 有笼统的数据模型来描绘富文本修正器的内容与状况
  1. Quill
  2. Slate
  3. CKEditor 5
  4. Draft.js
  5. ProseMirror
  6. wangEditor v5
L2
  1. 扔掉 contenteditable ,改为自己完成
  2. 扔掉 document.execCommand 操作内容,改为自己完成
  3. 自己完成排版引擎
  1. Google Docs

L0

L0 阶段的修正器首要是是依靠了浏览器原生的 contenteditable API 来完成修正,以 document.execCommand API 来完成多种操作,比方加粗、绑定链接、复制张贴等等。

优势:

  1. 技能门槛低。只需运用了以上两个 API ,就能够让网页具备修正才能。
  2. 根据浏览器原生修正才能,输入十分流通。
  3. 没有令人头疼的组合输入问题。

劣势:

  1. 相同操作在不同浏览器上会有不同完成。
  2. 输出富文本内容是 HTML ,不利于办理数据。
  3. 扩展杂乱的富文本很困难。
  4. 没有办法完成协同修正。

L1

现在大多数修正器结构都处于 L1 阶段,比较有代表性的便是 Quill、Slate、ProseMirror 和 Draft.js。它们首要有两个显着的特色:

  1. 依然依靠于 contenteditable API 用于内容修正,但不再依靠 document.execCommand API 来操作内容,改为自己完成。
  2. 有笼统的数据模型来描绘富文本修正器的内容与状况。

2012 – Quill

Web 富文本编辑器演进

Quill 是 API 驱动的富文本修正器结构,供给开箱即用的修正器体验。

Quill 的作者 Jason Chen 是一名华裔,Quill 其实算是 Jason 的一个 Side Project,最初是创办了一家公司,专门做相似 Google Docs 的协作修正器,因而自己写了 Quill 出来运用。

Quill 对 DOM Tree 以及数据的修正操作进行了笼统,从而实际运用时不需要咱们对 DOM 操作,而是经过 Quill 的 API 进行操作,对应的联系如下:

Editor Document ====> Parchment

DOM Node ====> Blot

有了这层笼统后,本来的对 DOM 的直接操作就变成了对 Blot 的操作,这些操效果 Delta 来表明。Quill 在 Delta 中扔掉了 DOM 的节点树的层次,因而完全看不出包裹文字的标签和节点联系,只要一个扁平化后的数组 ops

数据模型:

Web 富文本编辑器演进

{
    "ops": [
        {
            "attributes": {
                "bold": true
            },
            "insert": "Check"
        },
        {
            "insert": " "
        },
        {
            "attributes": {
                "link": "https://donaldxdonald.xyz/"
            },
            "insert": "this"
        },
        {
            "insert": " out ~"
        }
    ]
}

Delta 的扁平化结构其实是协同修正中的 OT 模型的一种完成,因而 Quill 也是生来便是为了协同修正而规划的。扁平化带来的长处是对功用提升有帮助,弊端则是在表明一些杂乱的嵌套内容时会比较费劲。

Quill 的特色是:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 引入了一层笼统的数据结构用以描绘内容以及行为
  3. 对协同修正支撑杰出
  4. 输出结构可所以字符串也可所以 Delta (JSON),但 Delta 作为数据模型可读性不高

2015 – ProseMirror

Web 富文本编辑器演进

Marijn 是 CodeMirror 修正器和 acorn 解析器的作者,前者已经在 Chrome 和 Firefox 自带的调试东西里运用了,后者则是 babel 的依靠。为了有更多的收入,Marijn 开端了新的项目,ProseMirror 。

Marijn 觉得当时市面上的开源修正器都没有一个选用他以为是抱负的办法,且许多仍是运用着旧的范式来规划,运用着 contentEditable来完成。这姿态开发者对文档内容能操控的范围就很小,而这又是很容易被用户和浏览器修正的。虽然 ProseMirror 仍是根据 contentEditable 完成修正功用了,究竟自己重新完成一套选区逻辑太麻烦了。

ProseMirror 是有 schema (范式)的,所以界说好了 schema 以后 ProseMirror 能够替你完成自动化 parser 。结构层面界说好了新引入一个 Node 需要什么特点和办法,比方 nodeFromJSON 办法做结构到 JSON 的转化,toDOM 办法界说怎么将结构数据转化为 DOM(有点相似 JSX )。ProseMirror 就在中间这一层做了 JSON 数据到 DOM 的变更办理。

数据模型:

Web 富文本编辑器演进

{
    "type": "paragraph",
    "content": [
        {
            "type": "text",
            "marks": [
                {
                    "type": "strong"
                }
            ],
            "text": "Check"
        },
        {
            "type": "text",
            "text": " "
        },
        {
            "type": "text",
            "marks": [
                {
                    "type": "link",
                    "attrs": {
                        "href": "https://donaldxdonald.xyz/",
                        "title": ""
                    }
                }
            ],
            "text": "this"
        },
        {
            "type": "text",
            "text": " out ~"
        }
    ]
}

ProseMirror 的特色是:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 更笼统的 JSON 文档模型。ProseMirror 只界说了可配置的模型结构,具体的结构能够在实际开发的时分自界说。
  3. 嵌套的树形结构。能支撑杂乱结构的内容。
  4. 对协同修正的杰出支撑。从诞生之初,ProseMirror 就开端关注着协同修正的支撑。
  5. 1.0 后加入了不行变数据,使得修正器的数据处理有了一个完整的数据流,稳定且可控。

2016.02 – Draft.js

Web 富文本编辑器演进

彼时还叫 Facebook 的 Meta 开源了 Draft.js ,既然都是同一个公司的,Draft.js 就在视图层方面运用了 React 烘托 UI 。这也是第一个 React + 修正器结合的案例,React 的盛行也让运用者能够快速地直接根据 Draft.js 进行二次开发

Draft.js 不仅外表有 React ,内里也是有很深的 React 的影子,相似 Redux 等状况办理的 EditorStateContentState ,在数据层运用 Immutable等特性。JS 目标的特点是能够随意赋值的,也便是 mutable 可变的。而相对地,不行变的数据类型不允许随意赋值,每次经过 Immutable API 的修正,都会生成一个新的引用。

Draft.js 的特色是:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 用 React 来完成视图层
  3. 内容的存储和烘托逻辑别离
  4. 运用 Immutable 数据
  5. 虽然也笼统了根据 JSON 的数据模型,可是关于嵌套数据的支撑有些弱

2016.06 – Slate

Web 富文本编辑器演进

此刻市面上已有许多修正器轮子在卷了,可是 Ian Storm Taylor 在开发自己的 CMS 产品时,依然觉得没有一个好用的修正器,他觉得这些修正器假如仅仅用来做一些简略的产品的话,是挺不错的了,可是假如想要开发像 Medium 、Google Docs 和 Dropbox Paper 这些大型运用的话,就太难太难了,所以就有了 Slate。

Slate 相同的是一个修正器结构,而不是开箱即用的修正器东西。作为后辈的 Slate 调集了长辈们的许多长处,从 Draft.js 那里参阅的 Immutable 数据、插件机制和 React 视图层,又从 ProseMirror 学习了嵌套数据结构和 Schema 束缚规矩。整合了许多修正器结构的中心特性,又加上结构理念先进和作者对架构的追求(时至今日 2022 ,依然是 beta 的状况,还没到 1.0),Slate 在社区上仍是比较受欢迎的。

数据模型:

Web 富文本编辑器演进

{
    "object": "block",
    "type": "paragraph",
    "nodes": [
      {
        "object": "text",
        "text": "This is editable "
      },
      {
        "object": "text",
        "text": "rich",
        "marks": [{ "type": "bold" }]
      },
      {
        "object": "text",
        "text": " text, "
      },
      {
        "object": "text",
        "text": "much",
        "marks": [{ "type": "italic" }]
      },
      {
        "object": "text",
        "text": " better than a "
      },
      {
        "object": "text",
        "text": "<textarea>",
        "marks": [{ "type": "code" }]
      },
      {
        "object": "text",
        "text": "!"
      }
    ]
}

此刻 Slate 的特色是:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 用 React 来完成视图层
  3. 支撑嵌套的 JSON 数据结构
  4. Immutable 数据
  5. 插件机制为中心
  6. 有束缚数据的 Schema

2019 – Slate 0.50+

Slate 在架构上进行了一个大更新,作者称 “整个结构都从头开端重新考虑了”,首要更新的点为:

  1. 将底层逻辑抽离出来 Slate Core ,与视图层别离
  2. 用 TypeScript 重写
  3. 简化插件机制,插件不再与烘托逻辑耦合
  4. 用简略的 JSON 目标替换 Immutable.js
  5. 自有概念和一些 Commands 更精简更笼统,改名为 Transforms

数据模型:

Web 富文本编辑器演进

{
    type: 'paragraph',
    children: [
      { text: 'This is editable ' },
      { text: 'rich', bold: true },
      { text: ' text, ' },
      { text: 'much', italic: true },
      { text: ' better than a ' },
      { text: '<textarea>', code: true },
      { text: '!' },
    ],
},

这时分 Slate 的特色是:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 十分简练的支撑嵌套的数据模型
  3. 整体架构选用纯函数 + 接口的方式,思路和代码都十分简练
  4. 插件机制支撑开发强壮的功用
  5. 整体的规划理念与 DOM 很像

2022 – Lexical

Web 富文本编辑器演进

因为 Draft.js 在以往兼容浏览器方面做了许多的脏活,且这些是现在不太必要的,一起为了改进开发者体验(许多开发者吐槽 Draft.js 不好用),Meta 又开源了一款新的修正器结构 Lexical ,旨在替换 Draft.js。

现在看来暂时没有什么特别创新的概念,首要也仍是吸收了其他修正器结构的长处:

  1. 依靠浏览器原生修正才能 contentEditable (L1)
  2. 保留了 Draft.js 中的一些概念(EditorState)
  3. 不与 React 绑定了,能够用各种结构完成视图层
  4. 整个结构挺简便的,几乎没有什么其他的依靠

L2

前面都有提到,凭仗浏览器的contenteditable API,能够快速地开宣布一款修正器出来,可是各种兼容问题太多了,别看现在大多数修正器结构还在 L1 ,其实早在 2010 年,财力雄厚的 Google 就已经开端扔掉浏览器的 contenteditable 了,新版 Google Docs 根据 Canvas 自己研发光标体系、文本布局体系和字体解析,因而除了自己的笼统数据结构外,连修正操作都是自己完成了,修正的出现效果一致了,那协同修正就自然瓜熟蒂落了。

Web 富文本编辑器演进

当然,Google Docs 的中心技能没有开源,这种拼钱的作业仍是得牢牢把握在自己手中,国内的腾讯文档和 WPS 等修正器也算是 L2 的,不一定是用 Canvas ,但思路都是自己完成修正布局功用,弃用浏览器的 contenteditable

总结

contenteditable is terrible, 可是修正器已经最小化了对它的运用,比之更为严峻的是,操作体系、浏览器、输入法彼此组合构成的紊乱生态 —— 一个修正器无法操控的,但产品又期望在上面开出繁花的生态。所以才说,Web 富文本修正器是前端的天坑之一。

参阅链接

  1. 为什么都说富文本修正器是天坑? – 知乎
  2. 有多大比例的前端工程师,能在合理的时间内独立开宣布一个足以供商业网站运用的文本修正器? – 知乎
  3. 开源富文本修正器技能的演进(2020 1024)
  4. ContentEditable窘境与破局
  5. Slate.js – 革命性的富文本修正结构 –
  6. Jason Chen: Building Editors in the Browser | JSConf.ar 2014
  7. Marijn Haverbeke: Salvaging contentEditable: Building a Robust WYSIWYG Editor | JSConf EU 2015
  8. ProseMirror
  9. Facebook open sources Lexical, an extensible text editor library – Hacker News
  10. 为什么 ContentEditable 很恐惧 – OSCHINA – 中文开源技能交流社区
  11. 怎么看待 Google Docs 将从 HTML 迁移到根据 Canvas 烘托? – 知乎
  12. 从盛行的修正器架构聊聊 Web 富文本修正器的窘境