slate源码解析(一)- 序言
笔者从大学时期就开始接触的前端,在刚去实习的时候就被导师安排去做内网的一个小富文本工具。之后从毕业后干的第一份工作游戏客户端,到现在做着可视化相关的前端工作,都有在做富文本相关的内容。可以说是和富文本编辑器(Rich Text Editor)有着不解之缘。
WYSIWYG
如无特别指出,该系列文章中所说的富文本都是指WYSIWYG模式的编辑器。WYSIWYG是英文"What You See Is What You Get"的缩写,翻译过来就是“所见即所得”。
这类产品中最久负盛名的就是Microsoft Word文档。Word文档提供了诸如字体、颜色、背景色、段落等针对文字样式的支持外,还有各种页面排版以及图片、视频插入等富文本功能。并且也完全满足WYSIWYG的原则(除了跨版本打开Word文档时那令人恼火的排版错乱问题)。
在Web端更是富文本应用大放异彩的地方。刚提到的Word文档很早之前就有了Web版本,除此之外还有大名鼎鼎Google Docs、前些年很火的笔记工具Notion等。国内近些年也兴起了众多类似模式的Web应用,如主打办公协作的腾讯文档、飞书文档,偏向于知识库管理的语雀等。
不同时代的富文本编辑器
回到开发者的视角;富文本应用可以说是前端中的“天坑”领域了。如果想最快的实现一个简易的富文本,那么只需在给DOM节点增加一个contenteditable=true即可让页面变为可编辑的模式。此外还有配套的API-execCommand可实现在编辑区域内撤销、回退、变更文本样式等功能。这么看来,既然有浏览器的这些支持,那么在Web端实现富文本应该并不是一件很难的事。 但坑就坑在了浏览器的兼容性上。各个浏览器厂商对于execCommand支持程度是不同的,为了屏蔽各浏览器的差异而提供一套统一的API给开发者使用,就必须额外地做许多工作。曾经非常著名的CKEditor就是基于该方式实现的。
对于开源界中数量众多的富文本库,个人钟意将其分为两大类:
旧时代的富文本编辑器:代表作CKEditor、TinyMCE、国内百度的UEditor以及众多基于JQuery使用的富文本插件。这些富文本编辑器的最大特点就是“开箱即用”。它们往往都没有太多内置的概念,如果你并没有深入应用的场景,那么遵循官方文档能以非常简单的方式引入便可以在页面中拥有一个可用的富文本编辑器了。并且它们都有预置的极其丰富的各式功能,一般都是在初始化编辑器时以配置的方式传入,以TinyMEC入门文档中的一段代码为例:

只需在构造函数中传入容器DOM的id和你需要的富文本工具栏的功能集数组,就可以展示出一个完备的富文本编辑器出来了。
而该类型编辑器的缺点非常明显:(1) 能够支持的功能局限在库本身所提供的功能集,可自定义的程度很低。(2) 没有做Data Model和View的拆分,对于编辑器内容的存储和导出往往是与HTML标签高度耦合的。虽然这种方式非常便于直接展示富文本内容,但如果想对存储内容做遍历分析或回显调整等操作的话则会存在诸多不便的地方。并且存储内容中包括太多的HTML标签和样式信息会大大增加数据的体积。现代化的富文本编辑器:本系列文章的主角 - Slate.js以及同样是基于React生态的Draft.js都可以称为现代化的富文本编辑器。
但这两者其实更应该叫做富文本编辑器框架。与上一代的富文本编辑器不同,Draft.js和Slate.js不提供任何“开箱即用”的功能。它们都实现了将Data Model和View层的拆分。在View这一层,两者都是基于React来做内容的自定义渲染。在Data Model层则是有一套框架自己定义的schema格式,在开发过程中主要精力就是关注在这套Data Model上以及从它们到View层的转换。
作为框架,它们没有任何现成可用的富文本功能,仅是提供一系列开发编辑器所需的基础“元件”和最基本的一套操作API。这就导致了相对于上代编辑器有了更高的上手门槛:哪怕是开发一个再简陋的富文本应用,都需要从零开始实现,因此也有着更大的心智负担和更多的代码量。
接下来
既然本系列文章是解析Slate.js的源码,先来看看Slate这个库中有哪些包:

我们开发者最需要的是两个包:
slate数据模型层。Slate最核心的部分,完全使用Typescript编写,包括了Slate数据模型的定义以及用于操作数据的对外接口。数据模型中的许多类型都是可拓展的(extendable),在后续讲解自定义类型的时候还会细说。slate-react视图层。负责和前端框架React的对接,渲染富文本内容及用户交互的处理。正是由于这种良好的架构,使得开发者除了可以选择直接使用官方的slate-react作为视图层,还可以在不同的前端框架下实现自己的视图层,slate-angular就是一个非常优秀的例子。
至于slate-history包,则是用于为编辑器提供撤销回退操作(undo/redo)的插件,不过笔者还并不打算开slate-history的坑: ) 该系列文章中主要是专注于slate和slate-react的源码解析。
笔者从0.62版本开始就开始调研Slate,当时可谓是一把辛酸泪: ( 各种花式Bug和浏览器相关的兼容问题数不胜数;并且出了问题后能搜索到的资料极少,慢慢翻Slate的issue板块也不一定能有解决方案,往往最后之只能用非常hack的方式绕过去。 而在该系列文章,笔者打算基于0.82版本的源码作解析。0.82距最新版本很近,这也是笔者正在使用,已经引入生产环境中并稳定运行了的,曾经已知的许多bug和问题都已经得到了解决: )。
小结
本篇并没有什么实际的干货,笔者只是从自己的角度概述了下在Web端上富文本的分代和它们中典型开源库的介绍,另外再提了下Slate下各个包的作用。但不着急,在下一篇我们就会真正进入到源码之中了:)
slate源码解析(一)- 序言的更多相关文章
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
- jQuery2.x源码解析(回调篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...
- HashMap 源码解析
HashMap简介: HashMap在日常的开发中应用的非常之广泛,它是基于Hash表,实现了Map接口,以键值对(key-value)形式进行数据存储,HashMap在数据结构上使用的是数组+链表. ...
随机推荐
- Microsoft.IO.RecyclableMemoryStream源码解读
一.RecyclableMemoryStreamManager 源码地址:https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStrea ...
- TIE: A Framework for Embedding-based Incremental Temporal Knowledge Graph Completion 增量时序知识图谱补全论文解读
论文网址:https://dl.acm.org/doi/10.1145/3404835.3462961 论文提出一种用增量学习思想做时序知识图谱补全(Temporal Knowledge Graph ...
- js 获取当前时间转换时间戳 (毫秒)
js 当前时间转换毫秒数 五种方式 var date = new Date().getTime(); var date = new Date().valueOf(); var date = +ne ...
- 解决MySQL Connector/ODBC驱动无法安装Error1918
1.问题描述 我在一台windows服务器上安装好mysql之后,再安装mysql的ODBC连接驱动时,报错如下: 2.解决方法 之所以出现安装失败是由于缺少Miscrosoft Visual C++ ...
- [C++标准模板库:自修教程与参考手册]关于vector
什么是vector 可以这样认为,vector就是一个动态的数组,其中的元素必须具备assignable(可赋值)和copyable(可拷贝)两个性质. vector的一些重要的性质 vector支持 ...
- Apache RocketMQ 5.0 笔记
RocketMQ 5.0:云原生"消息.事件.流"实时数据处理平台,覆盖云边端一体化数据处理场景. 核心特性 云原生:生与云,长与云,无限弹性扩缩,K8s友好 高吞吐:万亿级吞吐保 ...
- Python面向对象(上)
Python面向对象(上) python是一门面向对象的编程语言.何为对象?对象是类的实例.在生活中,任何一个事物都是一个对象,如牡丹花.牡丹花的类是花类,同样属于花类的还有荷花.月季花.金银花.菊花 ...
- 获取对象的方式-Calendar类的常用成员方法
获取对象的方式 java.utiL.calendar类:日历类caLendar类是一个抽象类,里边提供了很多操作日历字段的方法(YEAR.MONTH.DAY_OF_MONTH.HOUR )CaLend ...
- super与this关键字图解-Java继承的三个特点
super与this关键字图解 父类空间优先于子类对象产生 在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身.目的在于子类对象中包含了其对应的父类空 间,便可以包含其父类的成员,如果父类成 ...
- Windows静态库和动态库区别
个人建议:能使用静态库的就不要使用动态库,能使用隐式调用的就不要用显示调用. 注意: (1)动态库中的.lib文件叫做导入库,对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符 ...