#8 多人协作、富文本编辑器和Roam Research

2021/03/14 13:12

Marijn的博客

如果对多人协作、富文本编辑器、代码编辑器、代码解析器、JavaScript性能感兴趣,Marijn的博客不能错过。

Marijn是谁呢?下面这些库都是Marjin开发的。

  • Acorn: 一个用JavaScript开发的Parser,Babel、Webpack等工具都使用Acorn做代码解析。
  • CodeMirror: 世界上使用最广泛的基于浏览器的代码编辑器。比如Chrome Devtools、Github、Codepen等等,都用了CodeMirror。作者最近正在对它进行重写。
  • ProseMirror: 富文本编辑器。Confluence、Jira、Substack等编辑器都是基于ProseMirror开发的。

关于富文本编辑器

作者2015年对ProseMirror的介绍

我使用ProseMirror时就有一些疑问,作者为什么要单独设计Schema、为什么要拆很多小package等。这篇ProseMirror初版设计文章基本都解答了。

大部分富文本编辑器都基于contentEditable,但contentEditable不可靠。
ProseMirror的思想和React.js很像,即把文档抽象为一个状态,文档的所有修改都由代码改变状态而不是由contentEditable控制。状态修改后,产生一个新的状态。
把浏览器事件转成自定义的操作,就可以很容易兼容不同浏览器、处理剪贴板、回退、拖拽、输入法的预输入等等。
另一个是basecamp的trix的做法,把contentEditable想象成一个IO设备,而IO设备就是不稳定不可靠的。

ProseMirror自己设计Schema,而没有用类DOM结构。一个重要的原因是:最终输出的不一定是HTML、可能是Markdown、或者任意格式文档。
另外,一个段落里的文字是扁平结构,而不是像DOM是一棵树,处理文本样式比较方便。比如一个文字不允许被加粗两次,而DOM树是可以 <em><em>test</em></em> 的。

ProseMirror定义了 inputrules 模块,能够轻易地实现输入 1. 空格 自动转换为有序列表。

ProseMirror的种种设计思路,使得可以基于它几乎做出任何可协作的富文本编辑器(就像文本编辑器届的React.js)。比如SciFlow的学术协作、Confluence、Jira、Bitbucket的编辑器、纽约时报的CMS。

关于多人协作

ProseMirror设计之初就考虑了多人协作,这影响了history等模块的设计:因为协作中的undo不是撤销一个共享的history,而是撤销本地的上一次修改。
关于协作算法,文献中的和现实中的实现方式,是十分割裂的。
大部分文献考虑的是:完全分布式的协作、同时有多个人编辑、同样的角色、直接交换变更、并且都在同一个文档上。
而一个典型的Web系统,是客户端和服务端通信,由服务端来协调变更。
这两者面临的问题是截然不同的。如果你实现的是后者(非完全分布式),那么文献中提到的95%的问题,都不会出现。

文章里介绍了OT、CRDT的基本原理、二者的优劣、以及ProseMirror、CodeMirror分别用了什么方式。他们的Trade-Off分别是什么,以及都有哪些坑。

关于扩展性和插件系统

CodeMirror和ProseMirror的可扩展性都是非常强的。把可扩展性作为第一优先级考虑。这篇文章作者介绍了在设计插件系统时需要考虑的问题、常见的解决办法以及可能会遇到的挑战。

另外figma的文章也值得一读,是不同的角度:https://www.figma.com/blog/how-we-built-the-figma-plugin-system/

关于Roam Research

最近几个月一直使用Roam Research记录所有事情。Roam设计得非常精妙,很难找出结构上的缺陷。Block、Page、References等概念的设计,缺一不可。

Deep Dive Into Roam's Data Structure - Why Roam is Much More Than a Note Taking App

这篇文章详细的分析了Roam使用的数据库,数据结构,以及查询语法。
Roam使用的数据库是:Datomic。查询语法是Datalog。Datalog是Prolog的子集,看Wiki上大概是1977年就出现了,但我之前完全没有听说过它。

Roam内置了一个计算器 calc

发现Roam有很多组件是我平时几乎用不到的,比如看板、流程图、手绘、番茄时间等等。
最近发现计算器组件在记录菜谱时非常实用。我修改份数,就能自动计算每个原材料的量。比Google Sheets要更加直观。
image (1).png

自定义Roam

  • roam/css 页面写CSS代码,会把该CSS应用到整个Roam中。截图是我正在使用的主题
    Better Roam Research
  • roam/js 页面写JavaScript代码,会在每一个页面中执行该JavaScript代码。可以引入很多Roam扩展。比如 Roam42
  • roam/render 这个页面可以自定义Roam的组件。

这三个自定义页面使得Roam的可扩展性非常的强,让我对Roam的未来充满期待。