Skip to content

Commit

Permalink
25/01/18
Browse files Browse the repository at this point in the history
  • Loading branch information
WindRunnerMax committed Jan 18, 2025
1 parent 9739ebe commit 99316f2
Showing 1 changed file with 50 additions and 6 deletions.
56 changes: 50 additions & 6 deletions Backup/从零设计实现富文本编辑器.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

我也算是比较关注于各类富文本编辑器的实现,包括在各个站点上的编辑器实现文章我也会看。但是我发现这其中极少有讲富文本编辑器的底层设计,绝大多数都是将的应用层,例如如何使用编辑器引擎实现某某功能等。这些应用层的实现本身也会有一定复杂性,但是底层的设计却是更值得探讨的问题。

此外,我觉得富文本编辑器很类似于低代码的设计,准确来说是`No Code`的一种实现。本质上低代码和富文本都是基于`DSL`的描述来操作`DOM`结构,只不过富文本主要是通过键盘输入来操作`DOM`,而无代码则是通过拖拽等方式来操作`DOM`,我想这里应该是有些共通的设计思路。

而我恰好前段时间都在专注于编辑器的应用层实现,在具体实现的过程中也遇到了很多问题,并且记录了相关文章。然而在应用层实现的过程中,遇到了很多我个人觉得可以优化的地方,特别是在数据结构层面上,希望能够将我的一些想法应用出来。而具体来说,主要有下面的几个原因:

## 编辑器专栏
Expand Down Expand Up @@ -151,16 +153,58 @@
];
```

当然还有`Piece Table`
当然还有很多特别的数据结构设计,例如`vscode/monaco``piece table`数据结构。代码编辑器又何尝不是一种富文本编辑器,毕竟其是可以支持代码高亮的功能的,只不过类似`piece table`的结构我还没有太深入研究。

在这里我希望能够以线性的数据结构来表达整个富文本结构,虽然嵌套的结构能够更加直观地表达文档内容,但是对于内容的操作起来会更加复杂,特别是存在嵌套的内容时。以`slate`为例,在`0.50`之前的版本`API`设计非常复杂,需要比较大的理解成本,虽然之后将其简化了不少:

```js
// https://github.com/ianstormtaylor/slate/blob/6aace0/packages/slate/src/interfaces/operation.ts
export type NodeOperation =
| InsertNodeOperation
| MergeNodeOperation
| MoveNodeOperation
| RemoveNodeOperation
| SetNodeOperation
| SplitNodeOperation;
export type TextOperation = InsertTextOperation | RemoveTextOperation;
```

从这里可以看出来,`slate`对于文档内容的完整操作是需要`9`种类型的`Op`。而如果是基于线性结构的话,我们就只需要三种类型的操作,即可表达整个文档的操作。当然对于一些类似`Move`的操作,则需要额外的选区`Range`计算处理,相当于将计算成本移交到了应用层。

此外在`0.50`之前的版本`API`设计非常复杂,需要比较大的理解成本,虽然
normalize 很复杂,特别是脏路径标记
```js
// https://github.com/WindRunnerMax/QuillBlocks/blob/c24b9e/packages/delta/src/delta/interface.ts
export interface Op {
// Only one property out of {insert, delete, retain} will be present
insert?: string;
delete?: number;
retain?: number;

attributes?: AttributeMap;
}
```

此外,嵌套结构的`normalize`会变得很复杂,且变更造成的时间复杂度也会变高,特别是脏路径标记算法,以及标记后的数据处理也需要由上述`Op`处理。还有用户操作导致的嵌套层级无法非常好地控制,就要`normalize`过程时规范数据,否则例如粘贴`HTML`时就可能会出现大量的数据嵌套。

```js
[{
children: [{
children: [{
children: [{
children: [{
// ...
text: "content"
}]
}]
}]
}]
}]
```

视图相关
扁平的数据结构在数据处理方面会存在优势,而在视图层面上,扁平的数据结构表达结构化的数据会是比较困难的,例如表达代码块、表格等嵌套结构。但是这件事并非是不可行的,例如`Google Doc`的复杂表格嵌套就是完全的线性结构,这其中是存在很巧妙的设计在里边的。

但是基于扁平的数据结构,来表达结构化的数据会是比较困难的,例如表达代码块、表格等嵌套结构
最后还是有协同相关的实现,协同算法是富文本编辑器的可选模块。无论是基于`OT`的协同算法,还是`Op-Based CRDT`的协同算法,都是需要传输上述的`op`类型与数据的,那么很显然`9`种操作的`op`类型会比`3`种操作的`op`类型更加复杂

协同相关
因此,我希望能够以线性的数据结构来实现整个编辑器结构,这样`quill``delta`就是非常好的选择。但是`quill`是自行实现的视图层结构,并非是可以组合`react`等视图层的形式,组合这些视图层的优势就是可以直接使用组件库样式来实现编辑器,而避免了每个组件都需要自行实现。那么这里我准备基于`quill`的数据结构,来从零实现富文本编辑器核心层,并且像`slate`一样以此组合基本的视图层。

## 方案选型
实现`ContentEditable`的编辑器
Expand Down

0 comments on commit 99316f2

Please sign in to comment.