quill parchment

栏目: JavaScript · 发布时间: 5年前

内容简介:Parchment是Quill的文档模型。是一个和DOM树对应的平行树结构,给内容编辑器Quill提供有用的功能。一个Parchment 树是由Blots组构成。Blot是一个DOM节点的对应物。Blots可以提供结构,格式化,或内容。Attributor可以提供轻量级的格式化信息。Parchment tree是DOM tree的对应,二者关系紧密。

概念介绍

Parchment是Quill的文档模型。是一个和DOM树对应的平行树结构,给内容编辑器Quill提供有用的功能。

一个Parchment 树是由Blots组构成。Blot是一个DOM节点的对应物。Blots可以提供结构,格式化,或内容。Attributor可以提供轻量级的格式化信息。

Parchment tree是DOM tree的对应,二者关系紧密。

官方示例

LinkBlot

import Parchment from 'parchment';

class LinkBlot extends Parchment.Inline {
  static create(url) {
    let node = super.create();
    node.setAttribute('href', url);
    node.setAttribute('target', '_blank');
    node.setAttribute('title', node.textContent);
    return node;
  }

  static formats(domNode) {
    return domNode.getAttribute('href') || true;
  }

  format(name, value) {
    if (name === 'link' && value) {
      this.domNode.setAttribute('href', value);
    } else {
      super.format(name, value);
    }
  }

  formats() {
    let formats = super.formats();
    formats['link'] = LinkBlot.formats(this.domNode);
    return formats;
  }
}
LinkBlot.blotName = 'link';
LinkBlot.tagName = 'A';

Parchment.register(LinkBlot);

常见问题

富文本编辑器为什么需要自己的一套文档模型?

为什么必要?

为了提供一致的编辑体验,你需要一致的数据和可预测的行为。但是DOM在这两方面都不完美。所以现代的编辑器通过管理自己的文档模型来表示内容。

它的价值?

提供一致的数据和可预测的行为

如何构造出一套文档模型?如何与DOM建立关系?

需要定义出一套基础抽象节点类型, 一套基础的Attributor

ParentBlot, 
ContainerBlot,
LeafBlot,
EmbedBlot,
ScrollBlot,
BlockBlot, 
InlineBlot,
TextBlot

Attributor
ClassAttributor
StyleAttributor

然后会依赖于这些基础节点类型,来构造出一些实际节点类型。Quill中定义了一些实际节点

BlockBlot => Block
EmbedBlot => BlockEmbed
EmbedBlot => Break
ContainerBlot => Container
EmbedBlot => Cursor
EmbedBlot => Embed
InlineBlot => Inline
ScrollBlot => Scroll
TextBlot => Text

在新建Blot时会新建DOM节点,此时会将其挂到Blot的domNode属性上。

源码分析

基础设计

目录结构

- src
    - attributor
        - attributor.ts
        - class.ts
        - store.ts
        - style.ts
    - blot
        - abstract
            - blot.ts
            - container.ts
            - format.ts
            - leaf.ts
            - shadow.ts
        - block.ts
        - embed.ts
        - inline.ts
        - scroll.ts
        - text.ts
    - collection
        - linked-list.ts
        - linked.node.ts
    - parchment.ts
    - registry.ts

类图完整版

quill parchment

类图简易版

quill parchment

在parchment.ts中对外导出的有四类东西。

  • 节点Blot

    • ParentBlot 【父级节点】能对子节点进行增,删,改,移动,查
    • ContainerBlot 【容器节点】
    • LeafBlot 【叶节点】
    • EmbedBlot 嵌入式节点 【可格式化的叶节点】
    • ScrollBlot root【文档的根节点,不可格式化】
    • BlockBlot 块级 【可格式化的父级节点】
    • InlineBlot 内联 【可格式化的父级节点】
    • TextBlot 文本【叶节点】
  • 属性Attributor

    • Attributor 【一种代表格式的方法】
    • ClassAttributor 【使用classname模式来代表格式】
    • StyleAttributor 【使用内联样式来代表格式】
    • AttributorStore 【节点的attributes管理器】在BlockBlot InlineBlot中使用到了
  • 注册中心

    • Registry 【static blots = new WeakMap<Node, Blot>, attributes,classes,tags,types 】
  • 类型常量Scope

    • Scope

从一个例子看源码流程

let Inline = Quill.import('blots/inline');

class BoldBlot extends Inline { }
BoldBlot.blotName = 'bold';
BoldBlot.tagName = 'strong';

class ItalicBlot extends Inline { }
ItalicBlot.blotName = 'italic';
ItalicBlot.tagName = 'em';

Quill.register(BoldBlot);
Quill.register(ItalicBlot);

let quill = new Quill('#editor-container');

$('#bold-button').click(function() {
  quill.format('bold', true);
});
$('#italic-button').click(function() {
  quill.format('italic', true);
});
  1. 依赖于Quill提供的Inline类,构造了BoldBlot, ItalicBlot类,并注册到Quill, 会注册出formats/blod, formats/italic(是在Parchment导出的Registry中注册)
  2. 传入dom id, 构造出一个Quill实例quill。 这里会初始化大量属性,和它内部的模块,注册一些事件,传入id的dom会插入Quill自己的dom, 并绑定上了一些事件。
  3. 给指定id的dom绑定click事件,click触发时执行quill的格式化方法(里面会做一些判断,看是否有selection, 进行对应的格式化)。

quill.format分两种情况

A. 选中部分内容后,执行格式化。代码流程:

this.editor.formatText -> [this.scroll.formatAt, this.update(delta)] -> scrollBlot.formatAt -> parent.formatAt -> inline.formatAt -> inline.format(DOM修改)

quill.format('bold', true) 本质上会找到BoldBlot,然后执行它的format方法(格式化选中部分),同步更新delta, 真实的修改DOM,给selection添加strong标签。

delta同步和DOM同步是彼此独立的,delta同步相对简单一些(但会做一些组合优化)

const delta = new Delta().retain(index).retain(length, clone(formats));
return this.update(delta);`

B. 未选中内容,执行格式化。

未选中内容是对光标进行格式化。 this.selection.format -> this.cursor.format 。即会对光标后新写的内容对应格式化。

另一个例子

class DividerBlot extends BlockEmbed { }
DividerBlot.blotName = 'divider';
DividerBlot.tagName = 'hr';

$('#divider-button').click(function() {
  let range = quill.getSelection(true);
  quill.insertText(range.index, '\n', Quill.sources.USER);
  quill.insertEmbed(range.index + 1, 'divider', true, Quill.sources.USER);
  quill.setSelection(range.index + 2, Quill.sources.SILENT);
});

insertEmbed基本流程: quill.insertEmbed -> this.editor.insertEmbed -> [this.scroll.insertAt, this.update(delta)] -> 创建DOM,插到指定位置
同样的,创建DOM,更新delta都会做的。

参考资料

https://github.com/quilljs/pa...

https://quilljs.com/guides/cl...


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Two Scoops of Django

Two Scoops of Django

Daniel Greenfeld、Audrey M. Roy / CreateSpace Independent Publishing Platform / 2013-4-16 / USD 29.95

Two Scoops of Django: Best Practices For Django 1.5 is chock-full of material that will help you with your Django projects. We'll introduce you to various tips, tricks, patterns, code snippets, and......一起来看看 《Two Scoops of Django》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具