引言

对于富文本编辑器的兴趣由来已久,又因为所开发的产品需要使用到富文本编辑器的缘故,更加深对于web的富文本编辑器深入探究的渴望。
通常而言不需要从0打造一个富文本编辑器,有各种已有拿来即用的富文本编辑器(比如ckeditor,slatejs,quill,wangeditor等等)。这些编辑器有很多很多,大多数的产品如果需要用到富文本编辑器其实直接使用即可。也有一些商用富文本编辑器(比如google doc, 腾讯文档,wps,word网页版等等),这些编辑器就非常的复杂和前述的不再同一个层面,其功能也是非常强大的,当然这些编辑器是没法拿来即用的。
从具体的实现层面来说,现有的各种开源的编辑器从理念上是非常相似,大多是利用了浏览器的能力。但是上述的商用的产品其实现是跳脱了浏览器的提供的能力。下面将具体谈谈这些基于web技术的不同的编辑器的实现方式,从比较宏观的角度。
对于不同的实现方式,都会从下面这些角度来探究:

  • 数据、视图
  • 布局排版
  • 光标、选中
    大致以实现的难易程度,有易到难的层层递进来探究其具体的实现方式。难易程度的判断大致是以‘使用了多少浏览器自身的富文本能力’为判断标准,可能认为使用浏览器提供的富文本能力越多,实现的难度越小,可控度也越小。

完全使用浏览器自带的能力

现有编辑器例子:wangeditor

完全围绕着浏览器提供的能力来做封装。

  • contenteditable提供dom可编辑器的能力
  • selection提供对于富文本内容的选中的能力
  • execcommand提供一套命令系统

实现方式比较简洁、直接,只用围绕这三点,是一种在浏览器提供的框框中做增量的开发。

  • 数据、视图:dom、h5
  • 布局排版:浏览器的布局排版
  • 光标、选中:浏览器自带

这种方式可以定制,开发可控的地方非常的有限。

使用部分浏览器自带的富文本能力

编辑器例子:quill,slatejs,ckeditor,ueditor等等,这里可以列一大堆,现有的比较知名的开源的编辑器和一些产品的编辑器都是在此列

首先execcommand第一个被舍弃,contenteditable和selection没法抛弃,不过这里具体的实现有了很多分化,比如还会使用到浏览器的MutationObserver的能力等等。

数据驱动型

这种方式是给整个文档定义一套完整的数据结构,所有的操作首先是更改这套数据结构,然后再变更部分同步渲染到dom。

contenteditable被视作是一个事件io的入口,监听键盘、鼠标等等事件,但不交由浏览器处理,而是编辑器自己来处理。

  • 数据、视图:自定义的数据层结构,自定义视图层负责处理事件更改数据,然后交给ui层渲染
  • 布局排版:交给浏览器自动完成
  • 光标、选中:浏览器提供, contenteditable, range api

这种方式可能导致的后果是数据和渲染的不同步。

直接修改Dom型

和数据驱动型的方式最大的区别在于,没有一个自定义的数据结构,而是直接使用html/dom本身作为数据结构。

contenteditable被视作是一个事件io的入口,监听键盘、鼠标等等事件,使用range api获取选中内容。自定义一套命令系统,直接调用dom api来进行修改。

  • 数据、视图:html/dom
  • 布局排版:交给浏览器自动完成
  • 光标、选中:浏览器提供, contenteditable, range api

其他

上述两种方式基本概括了实现方式,但是还是有一些编辑器在细节上会有些不同。这里列举一些:

比如像quill,通过使用MutationObserver来处理dom的变更,所以不会完全的block掉contenteditable的事件输入。

完全不使用浏览器自带的富文本能力

编辑器例子:Google docs, 腾讯文档, WPS在线版, Word在线版等等

到了这个级别的编辑器,所有的能力都不依赖浏览器的,而浏览器只是提供了一个展示交互方式而已!!

首先要知道如果不使用任何浏览器的富文本能力,到底需要实现一些什么功能编辑器才能work?从直观表面来看

  • 光标:肯定要有光标一闪一闪才能知道哪里开始编辑
  • 选中:要能够选中富文本内容
  • 排版:富文本该怎么排版

这些直观功能,窥视到后面的细节,会有一些这样的疑问:

  • 怎么计算文字大小?
  • 光标该怎么左右上下移动?
  • 输入法怎么跟随输入的位置?
  • 键盘输入怎么监听?
  • 一行文字的换行展示?
  • 当没了contenteditable,rangapi的时候 等等一些列的问题就来了

实现概览

先把这些细节放一放,从上帝直接,大的流程看还是这么一个流程

  • 一套数据结构,存放了文档的所有信息
  • 一套布局排版系统,把拿到的数据进行一系列的操作排版输出到ui,进行渲染
  • 一套事件监听处理机制
  • 选中、光标等其他功能

难点就在于没一点都得完全自主的实现,而且这些功能都是环环相扣的,并且要考虑可维护性、可扩展性等等

一些实现细节

  • 光标通过一个 1px的div通过 animation来控制闪烁
  • 事件是通过一个隐藏的input/textarea来监听
  • 文字的宽度通过一个隐藏的div,传入所有的样式,然后动态计算出来

这里只是简单罗列了几点

总结

越往下探究,发现一个富文本编辑器的难度是完全超出自己想象的存在,当我们使用的时候觉得如此自然,其背后是巨大的积累过程。以上的内容也只是惊鸿一瞥,并没有太深入到细节之中,只是有一个概念和了解。