引言
对于富文本编辑器的兴趣由来已久,又因为所开发的产品需要使用到富文本编辑器的缘故,更加深对于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,传入所有的样式,然后动态计算出来
这里只是简单罗列了几点
总结
越往下探究,发现一个富文本编辑器的难度是完全超出自己想象的存在,当我们使用的时候觉得如此自然,其背后是巨大的积累过程。以上的内容也只是惊鸿一瞥,并没有太深入到细节之中,只是有一个概念和了解。