实现一个富文本编辑器是一个复杂的项目,涉及到前端和后端的多个方面。以下是一个简化的实现方案,我们将使用HTML、CSS和JavaScript来构建一个基本的富文本编辑器。### 1. HTML结构首先,我们需要一个容器来放置编辑器:```htmlRich
摘要:数据模型的设计是编辑器的核心基础,其直接影响了选区模型、DOM模型、状态管理等模块的设计。例如在quill中的选区模型是index + len的表达,而slate中则是anchor + focus的表达,
数据模型的设计是编辑器的核心基础,其直接影响了选区模型、DOM模型、状态管理等模块的设计。例如在quill中的选区模型是index + len的表达,而slate中则是anchor + focus的表达,这些都是基于数据模型的设计而来的。因此我们从零实现的富文本编辑器就需要从数据模型的设计开始,之后就可以逐步实现其他模块。
开源地址: https://github.com/WindRunnerMax/BlockKit
在线编辑: https://windrunnermax.github.io/BlockKit/
项目笔记: https://github.com/WindRunnerMax/BlockKit/blob/master/NOTE.md
从零实现富文本编辑器项目的相关文章:
深感一无所长,准备试着从零开始写个富文本编辑器
从零实现富文本编辑器#2-基于MVC模式的编辑器架构设计
从零实现富文本编辑器#3-基于Delta的线性数据结构模型
Delta
在先前的架构设计中我们已经提到了,我们实现的扁平数据结构且独立分包设计,无论是在编辑器中操作,还是在服务端数据解析,都可以更加方便地处理。相比较来说,嵌套的数据结构能够更好地对齐DOM表达,然而这样对于数据的操作却变得更加复杂。
因此在数据结构的设计上,我们是基于quill的delta结构进行了改造。最主要的部分是将其改造为immutable的实现,在编辑器中实际是维护的状态而不是本身的delta结构。并且精简了整个数据模型的表达,将复杂的insert与Attribute类型缩减,那么其操作逻辑的复杂度也会降低。
delta是一种简洁而功能强大的格式,用于描述文档内容及其变化。该格式基于JSON,不仅便于阅读,同时也易于机器解析。通过使用delta描述的内容,可以准确地描述任何富文本文档的内容和格式信息,避免了HTML中常见的歧义和复杂性。
delta由一系列操作组成,这些操作描述了对文档进行的更改。常见的操作包括insert、delete、retain。需要注意的是,这些操作不依赖于具体的索引位置,其总是描述当前索引位置的更改,并且可以通过retain来移动指针位置。
delta既可以表示整个文档,也可以表示对文档进行的更改。那么在这里我们将delta的主要类对象及相关操作逻辑进行描述,特别是在编辑器中实际应用场景,以及主要的改造和相关类型声明。
insert
insert方法是将数据插入到delta的操作,这就是delta。当描述整个文档内容时,整个数据的内容应该全部都是insert。首个参数是要插入的文本内容,第二个参数是可选的属性对象,用于描述文本的格式信息。
const delta = new Delta();
delta.insert("123").insert("567", { a: "1" });
// [{"insert":"123"},{"insert":"567","attributes":{"a":"1"}}]
原始的insert参数是可以对象类型的Embed结构,这种结构可以表达Image、Video、Mention等非文本结构的数据,而属性AttributeMap参数是Record<string, unknown>类型,这样用来描述复杂属性值。
在这里我们将其精简了,insert参数仅支持string类型,而具体的schema则在编辑器的初始化时定义,格式信息则收归于Attrs中描述。而AttributeMap则改为Record<string, string>类型,并且可以避免诸如CloneDeep、isEqual等对于复杂数据结构的实现。
其实在EtherPad中就是将Attribute就是[string, string]类型,在这里我们也是使用了类似的设计。在这种基础结构设计下,我们更推荐将属性值扁平地放置于attributes属性中,而不是使用单个属性值作为key,将所有属性值嵌套地放置于value中。
export interface AttributeMap {
[key: string]: string;
}
delta整个名字通常会用于描述变更,那么除了描述整个文档内容外,当然还可以描述文档内容的变更。不过应用变更的内容需要用到compose,这个方法的描述我们在后边再看。
