富文本编辑器的层级

  • L0
  • L1: 兼容性层面的优化, 自行实现 undo/redo 栈;
  • L2: 移动端/国际化适配;

一个好的框架设计: 尽量少的提前做的假设限制!

L0

  1. 给元素上加上 contenteditable 属性
  2. 给选中元素执行 document.execCommand()

另外一种方法是内嵌 iframe, 并将其 designmode 属性设置为 true。(有跨域问题, 日后填坑)

Slate 框架

调试

可以使用 editor.value.toJSON() 打印 tree 结构, 便于调试;

slate 设计模式

  • 兜底原则
    • normalizeNode, 除指定节点外都走该 normalize 机制;

有序列表的设计

  1. 小圆点 + 数字作为一个结构;
  2. 点击有序列表, 当前节点的同级兄弟节点都会变为有序列表;
  3. 点击可以取消有序列表;
  4. 当在有序列表的节点建立兄弟节点或子节点时, 自动设置为有序列表;
  5. 子节点规则 1 —— 1.1 —— 1.1.1 —— 1.1.1.1;
  6. 当把某个子节点从有序节点变为无序节点时, 该节点的兄弟节点也变为无序; 该节点的子节点从 1 开始计数;
  7. 原则: 兄弟节点必须都为有序或无序, 子节点可以无序也可以有序;

结论:

  1. 因此对 selection 是不敏感的, 即操作一个都会同时对同层级所有节点生效;

slate 开发坑点

  • 一种方式开发完之后, 在某些情况下是不满足的, 因此需要返工。
    • example: 输入 @、# 进行筛选最初是使用 set 方式实现的, 一开始这样设计是没问题的, 但是考虑未来在多人协作过程中会造成一份数据的互相影响, 因此将其重构调整为将筛选的数据绕开 set 的方式, 取而代之的是外面传递进来。

small tip

small tip in slate editor

When input b after a in page #slate #editor.

console.log(editor.value.texts.get(0)) // 'a';
requestAnimationFrame(() => {
console.log(editor.value.texts.get(0)) // 'ab'
})

Web Excel

  • Google Sheets
  • Office 365
  • WPS Web
  • 钉钉表格
    • 可扩展的表格
      • 棋盘模型
        • Range 模型
    • 协同的表格
      • 名词
        • cp(Check Point): 全量表格数据
        • op(Operation): 单次操作的表达
      • OT(Operation Transform) 是协同算法的关键。
      • 上限更高的 COT 调度算法要求 OT 同时运行在服务端与客户端
      • Rust + WASM 实现 OT, 已验证最小闭环
    • 高性能的表格
      • 选择 canvas 渲染表格主界面, 不用 DOM 的原因是浪费在 DOM Pipeline 的开销很大
      • 分层渲染管线
      • 双缓冲画布, 支持流畅的滚动
      • 自研排版引擎

协同编辑

  • etherpad-lite

    • 基于 OT 开发的从底层支持协同编辑的框架。
    • 代表: 飞书、石墨
    • 问题: 十年前代码、技术栈陈旧
    • 核心方法:
      • apply
      • compose
      • follow: 解决两个 changeset 冲突问题
      • inverse: 反操作, undo
  • Changeset

    • 代表对文档的一次修改
    • eg: Z:4>1=1*0+1$2
      • Z: 代表是一个 Changeset
      • 4>1: 源文档 4 个字符长度, 本次修改增加一个字符
      • =1: 跳过第一个字符
      • *0+1: 插入一个字符, 并且带上属性 0
      • $2: 插入的具体字符
    • Changeset Iterator, 解析上述 Changeset 的库
  • 渲染流程

    • 内联属性
    • 行属性: 标题
    • 块属性: 图片
  • 问题一: follow 为什么要对 A 与 B 都要处理, 可否只对一处进行处理?

  • 问题二: etherpad-lite 服务端与客户端协同的处理? (要有一个 master)

    1. 服务端设置为 true
    2. 客户端设置为 false
  • 问题三:

    • 弱网情况下, 如何优化协同问题? (比如输入拼音, 被截断, 无法删除)

link