什么是Quill?

Quill是一款由API驱动的强大的JavaScript富文本编辑器。它主要的优点是有良好的兼容性和可扩展性,支持市面上几乎所有的主流浏览器。

官方站点:https://quilljs.com/playground/

背景

最近在项目中用到了Quill富文本编辑器,所以有必要了解Quill的概念和用法。经过阅读Quill的官方文档以及该作者发布的文章,将了解到的相关知识做了整理。

安装

Quill

在使用Quill初始化编辑器之前,首先需要安装Quill。Quill的安装分为两种情况:如果项目中没有用到Webpack等前端编译打包工具,我们需要将Quill资源文件加载到html中。

  1. <!-- 引入css文件 -->
  2. <link href="https://cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet">
  3. <!-- 自定义编辑器工具栏 -->
  4. <div id="toolbar">
  5. <button class="ql-bold">Bold</button>
  6. <button class="ql-italic">Italic</button>
  7. </div>
  8. <!-- 创建编辑容器 -->
  9. <div id="editor">
  10. <p>Hello World!</p>
  11. </div>
  12. <!-- 引入js文件 -->
  13. <script src="https://cdn.quilljs.com/1.0.0/quill.js"></script>

如果项目中使用了Webpack,则可以通过npm包管理工具安装Quill:

  1. npm install quill -D

待安装完成后将Quill导入项目文件中:

import Quill from 'quill/core';

import Toolbar from 'quill/modules/toolbar';
import Snow from 'quill/themes/snow';

import Bold from 'quill/formats/bold';
import Italic from 'quill/formats/italic';
import Header from 'quill/formats/header';

Quill.register({
  'modules/toolbar': Toolbar,
  'themes/snow': Snow,
  'formats/bold': Bold,
  'formats/italic': Italic,
  'formats/header': Header
});


export default Quill;

初始化

在Quill安装工作做好之后就可以初始化编辑器了:

var options = {
  debug: 'info',
  modules: {
    toolbar: '#toolbar'
  },
  placeholder: 'Compose an epic...',
  readOnly: true,
  theme: 'snow'
};
var editor = new Quill('#editor', options);

Quill初始化时提供了一些配置项可供选择,options内容如下:

属性 类型 描述 默认值
bounds 可以设置为DOM元素或代表DOM元素的CSS选择器,用来包含编辑器的UI元素(比如tooltips等等),当前只考虑左右边界。d document.body
debug debug的快捷方式。注意,debug是一个静态方法并且可能影响当前页面内Quill编辑器实例。默认只显示警告和错误信息。

| warn | | formats | | 编辑器允许的格式列表白名单。完整的列表,请见Formats。 | | | modules | | 需要引入模块的集合及它们各自的选项。详情见See Modules。 | | | placeholder | | 当编辑器为空时显示的占位符文字。 | | | readOnly | | 是否将编辑器实例设置为只读模式 | false | | scrollingContainer | | 当默认的’ql-editor’元素已经被自定义CSS改变,可以设置为DOM元素或代表DOM元素的CSS选择器,指定有滚动条(overflow-y: auto)的容器。 | null | | strict | | 在semver的严格解释下,一些改进或修改被要求一个大版本的bump。它们通过strict严格标志来防止对Quill版本号进行小的更改。 | true | | theme | | 使用主题的名称。这个内置的选项是”bubble”或”snow”。一个无效或错误的值将加载默认的最小化主题。注意,主题的样式表任需要手动包含 | |

Parchment

Parchment是抽象文档模型,是与DOM树相对应的树形结构,Parchment树由若干个Blot组成。Parchment为Quill提供了许多有用的功能。可以使用npm包管理器安装Parchment:

npm install parchment -D

Parchment安装完成后将Parchment导入到文件中使用:

import Parchment from 'parchment';

let Align = new Parchment.Attributor.Class('align', 'blot-align');
Parchment.register(Align);

let node = document.createElement('div');
Align.add(node, 'right');
console.log(node.outerHTML);  // Will print <div class="blot-align-right"></div>

参考文档:https://github.com/quilljs/parchment#parchment—

Delta

Delta是一个扁平数组,用于保存编辑器中的内容。Delta数组中的每一项表示一次操作,它的变化直接影响编辑器中的内容变化。可以对Delta进行添加、删除和保留操作。

const delta = new Delta([
  { insert: 'Gandalf', attributes: { bold: true } },
  { insert: ' the ' },
  { insert: 'Grey', attributes: { color: '#ccc' } }
]);

const death = new Delta().retain(12)
                         .delete(4)
                         .insert('White', { color: '#fff' });

参考文档:https://github.com/quilljs/delta#delta—

Blot

Blot是Parchment文档的重要组成部分,用户可以通过操作Blot来实现对编辑器内容的修改,无需操作真实DOM。Blot是DOM Node对应物。Blot包含对应的结构、样式和内容。Blot对象拥有以下属性:

属性名称 描述
parent Blot父节点,若没有则为null
prev 上一个同级Blot,若没有则为null
next 下一个同级Blot,若没有则为null
scroll 顶级Blot
domNode 当前Blot对应的真实DOM节点

Blot.create

Blot类中存在静态方法create,每个Blot的创建的过程中会调用creae方法,在该方法中会返回创建后的真实DOM节点,但该节点此时并未插入文档。如果想要修改真实DOM节点的属性,可以在create方法中修改。

import Block from 'quill/blots/block'

class MyBlock extends Block {

    static create(initialValue) {
      const node = super.create()
    node.classList.add('my-class')
    return node
  }
}

用户通过复制粘贴创建的内容,会直接将剪切板中的html结构添加到编辑器中,不会通过create方法

构造函数

Blot通过domNode在构造函数中实例化。在构造函数中,可以增加比如事件绑定等操作。

import Block from 'quill/blots/block'

class MyBlock extends Block {
    constructor(domNode) {
      super(domNode)
    this.addEvents(domNode)
  }
    static create(initialValue) {
      const node = super.create()
    node.classList.add('my-class')
    return node
  }
  addEvents(domNode) {
      domNode.addEventListener('click',()=>{
        //...
    })
  }
}

注册Blot

添加完自定义Blot后,需要将该Blot注册到Quill中,这样才会被编辑器识别。

import Quill from 'quill';
import Block from 'quill/blots/block'

class MyBlock extends Block {
    constructor(domNode) {
      super(domNode)
    this.addEvents(domNode)
  }
    static create(initialValue) {
      const node = super.create()
    node.classList.add('my-class')
    return node
  }
  addEvents(domNode) {
      domNode.addEventListener('click',()=>{
        //...
    })
  }
}

MyBlock.className='my-block'
MyBlock.blotName='MyBlock'
MyBlock.tagName='div'

Quill.register(MyBlock)

创建Blot必须使用Parchment.create方法创建,使用new关键字实例化会影响到Blot生命周期正常工作

插入Blot

先使用Parchment.create创建blot节点,再利用insertInto(parentBlot,refBlot)方法可以将blot插入到父节Blot中。

const newBlot = Parchment.create("someBlotName", initialBlotValue);
const parentBlot = /* Get a reference to the desired parent Blot in some way */;
newBlot.insertInto(parentBlot);

除了上述,还可以使用insertAt(index,name,value)insertBefore(childBlot,refBlot)也可以插入Blot节点。

扩展

  • vue-quill-editor
  • quill-image-extend-module
  • quill-image-resize-module
  • quill-image-drop-module
  • quill-better-table
  • quilljs-table

总结

Quill是一款非常优秀的Javascript富文本编辑器,与其它富文本编辑器相比最大的区别是提供了例如ParchementBlotDelta等抽象模型,使用户可以在不直接操作底层DOM基础上轻松修改编辑器内容。同时它的可扩展性也非常强,用户可以根据自己需要定制各个功能模块。