Quill 的 Parchment 文档对象模型

1,365 阅读2分钟

What ? 首先来看它是什么东西

官方定义是:Parchment is Quill's document model. It is a parallel tree structure to the DOM tree, and provides functionality useful for content editors.

简单的理解就是由众多 Blot 组成的,与 DOM 树类似的一个自定义的文档结构,那么 Blot 又是什么呢?下面简单分析下 Blot

How?了解 Blots 的工作过程

  • 定义:Parchment 结构文档的抽象组成部分
  • Blot 的必要组成部分(对象引用)
    • scroll - 根节点
    • parent - 父节点
    • prev - 前一个兄弟节点
    • next - 下一个兄弟节点
    • domNode - blot 对应的 DOM 节点
  • 生命周期和API
    • Creation,创建 Blot 由很多步骤,但是可以统一调用 Parchment.create() 来创建
        • Blot.create() , 使用了 createElement() 方法来初始化创建 DOM 节点,此时只是返回创建了的节点,还未添加到 DOM 树上
        • constructor(domNode)
    • Registration,使用 Parcement 前需要先使用 Parchment.register() 对其进行注册,在Qulljs 中的 Quill.register 会调用 Parchment.register()
    • Insertion and Attachment,创建并注册好 Blot 之后,需要把其添加到 Parchment 和 DOM 文档中
      • insertInto(parentBlot, refBlot),将创建好的 Blot 插入到父 Blot 的 children 末尾,并且这个 Blot 的 domNode 对象会被添加到 parentBlot 的 domNode 中
      • insertAt(index, name, value),会调用 Parchment.create() 将Blot插入到传入的具体位置中
      • insertBefore(childBlot, refBlot),类似 insertInto ,将 refBlot 插入到 childBlot 前面的位置中
      • attach(),将父 Blot 的 ScrollBlot 根节点添加到当前 Blot 的 scroll 属性中
    • Updates and Optimization,使用 ScrollBlot 创建了 MutationObserver 对象来监听,管理 Blots 在 contenteditable 属性内部所做的更新。并且 ScrollBlot 还会追踪 MutationRecords 中的变化,如果 MutationRecords 变化的 target 和 Blot 的 domNode 是同一个节点,那么就会调用 Blot 的 update() 方法更新当前 domNode
      • update(mutations: MutationRecord[], sharedContext: Object),update() 方法会根据不同 Blot 类型做不同的实现
        • ContainerBlot
          • 检查直接子节点的修改
          • 检查 Blot 的 domNode 是否被删除,如果是则 remove 掉当前 Blot
          • 将已经添加 DOM 节点添加该 Blot
        • TextBlot
直接在 DOM 节点用新的内容替换掉 DOM 节点的内容
        • EmbedBlot
Parchment 中的 EmbedBlot 内部没有实现 update() 方法,与 Quill 中的 BlockEmbed一样无法控制其 DOM 子节点的变化
    • optimize(context),在 update() 方法执行成功后调用,需要注意的是,optimize 不应该改变 value 的长度,这样有利于降低文档的复杂度。或者是说文档的 Delta 数组长度应该在 optimize 前后保持一致。具体的优化规则待补充:
    • Deletion and Detachment
      • remove(),删除 Blot ,从 DOM 树中删除 Blot 的 domNode 属性,然后调用 detach() 方法
      • removeChild(blot),删除子 Blot,该方法只对 ContainerBlot 有用,清空 Blot 的 children 数组
      • deleteAt(),删除传入位置的 Blot 或者内容,方法内部调用了 remove()
      • detach()
  • 常用的 Blot
    • ContainerBlot(下面的会继承我)
      • ScrollBlot,最高层级的 ContainerBlot ,里面包含了所有其他 Blot
      • BlockBlot
      • InlineBlot
      • ScrollBlot
  • EmbedBlot
  • TextBlot


未完...