Skip to content

Commit

Permalink
25/01/20
Browse files Browse the repository at this point in the history
  • Loading branch information
WindRunnerMax committed Jan 20, 2025
1 parent 4f48350 commit d852ac5
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 2 deletions.
17 changes: 15 additions & 2 deletions Backup/从零设计实现富文本编辑器.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
富文本编辑器是允许用户在输入和编辑文本内容时,可以应用不同的格式、样式等功能,例如图文混排等,具有所见即所得的能力。与简单的纯文本编辑组件`<input>`等不同,富文本编辑器提供了更多的功能和灵活性,让用户可以创建更丰富和结构化的内容。现代的富文本编辑器也已经不仅限于文字和图片,还包括视频、表格、代码块、附件、公式等等比较复杂的模块。

- 开源地址: <https://github.com/WindRunnerMax/QuillBlocks>
- 在线编辑: <https://windrunnermax.github.io/QuillBlocks/>
- 项目笔记: <https://github.com/WindRunnerMax/QuillBlocks/blob/master/NOTE.md>

## Why?
Expand All @@ -13,7 +14,7 @@

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

## 编辑器专栏
### 编辑器专栏
纸上得来终觉浅,绝知此事要躬行。

我的博客是从`20`年开始写的,记录的内容很多,基本上是想到什么就写什么,毕竟是作为平时学习的记录。然后在`24`年写了比较多的富文本编辑器的文章,主要是整理了平时遇到的问题以及解决方案,集中在应用层的设计上,例如:
Expand Down Expand Up @@ -202,6 +203,8 @@ export interface Op {

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

此外,如果我们需要实现在线文档的编辑器的话,在整个管理流程中可能会需要`diff`,即取得两边数据结构的增删改。这种情况下扁平的数据结构能够更好地处理文本内容,而`JSON`嵌套结构的数据则会麻烦很多。还有一些其他关于数据处理方面的周边应用,整体复杂度都要提升不少。

最后还是有协同相关的实现,协同算法是富文本编辑器的可选模块。无论是基于`OT`的协同算法,还是`Op-Based CRDT`的协同算法,都是需要传输上述的`op`类型与数据的,那么很显然`9`种操作的`op`类型会比`3`种操作的`op`类型更加复杂。

- [OT.js: Text 数据类型](https://github.com/Operational-Transformation/ot.js/blob/e9a3a0e/lib/text-operation.js#L39)
Expand Down Expand Up @@ -336,9 +339,18 @@ data:text/html,<div contenteditable style="border: 1px solid black"></div>

这个`warning`的意思是,`React`无法保证`ContentEditable`中的`children`不会被意外修改或复制,这可能是不被意料到的。也就是说除了`React`本身是会需要执行`DOM`操作的,使用了`ContentEditable`之后,这个行为就变的不受控了,自然这个问题同样会出现在我们的编辑器中。

此外还有一些其他的行为,例如下面的例子中,我们无法从`123`字符选中到`456`上。也就是这里存在跨越`ContentEditable`节点了,就不能够正常使用浏览器的默认行为来处理,虽然这个处理是很合理的,但毕竟也会对我们实现`blocks`编辑器造成一些困扰。

```html
<div contenteditable="false">
<div contenteditable>123</div>
<div contenteditable>456</div>
</div>
```

那么其实我们是可以避免使用`ContentEditable`的,设想一下即使我们没有实现编辑器,同样是可以选择页面上的文本内容的,就是我们普通的选区实现。那么如果借助原生的选区实现,然后在此基础上实现控制器层,就可以实现完全受控的编辑器。

但是这里存在一个很大的问题,就是内容的输入,因为不启用`ContentEditable`的话是无法出现光标的,自然也无法输入内容。而如果我们想唤醒内容输入,特别是需要唤醒`IME`输入法的话,浏览器给予的常规`API`就是借助`<input>`来完成,因此我们就必须要实现隐藏的`<input>`来实现输入。
但是这里存在一个很大的问题,就是内容的输入,因为不启用`ContentEditable`的话是无法出现光标的,自然也无法输入内容。而如果我们想唤醒内容输入,特别是需要唤醒`IME`输入法的话,浏览器给予的常规`API`就是借助`<input>`来完成,因此我们就必须要实现隐藏的`<input>`来实现输入,实际上很多代码编辑器例如 [CodeMirror](https://github.com/codemirror/dev/) 就是类似实现

但是使用隐藏的`<input>`就会出现其他问题,因为焦点在`input`上时,浏览器的文本就无法选中了。因为在同个页面中,焦点只会存在一个位置,因此在这种情况下,我们就必须要自绘选区的实现了。例如钉钉文档、有道云笔记就是自绘选区,开源的 [TextBus](https://github.com/textbus/textbus) 同样采取了这种实现。

Expand Down Expand Up @@ -385,6 +397,7 @@ data:text/html,<div contenteditable style="border: 1px solid black"></div>

## 参考
- <https://github.com/w3c/editing>
- <https://zhuanlan.zhihu.com/p/226002936>
- <https://zhuanlan.zhihu.com/p/407713779>
- <https://zhuanlan.zhihu.com/p/425265438>
- <https://zhuanlan.zhihu.com/p/259387658>
Expand Down
2 changes: 2 additions & 0 deletions RichText/初探富文本之文档虚拟滚动.md
Original file line number Diff line number Diff line change
Expand Up @@ -892,10 +892,12 @@ https://github.com/WindrunnerMax/EveryDay
## 参考
```
https://web.dev/articles/lcp
https://developer.mozilla.org/zh-CN/docs/Web/CSS/overflow-anchor
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserverEntry
https://developer.mozilla.org/zh-CN/docs/Web/API/History/scrollRestoration
https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntriesByType
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect
https://arco.design/react/components/list#%E6%97%A0%E9%99%90%E9%95%BF%E5%88%97%E8%A1%A8
```

0 comments on commit d852ac5

Please sign in to comment.