富文本编辑器Tiptap系列教程——Tiptap模块&概念详解

这是我参与「掘金日新计划 · 6 月更文挑战」的第 4 天,活动详情:juejin.cn/pos…

接上篇 富文本编辑器Tiptap系列教程——5分钟搭建基于Tiptap的富文本编辑器,本节我们主要讲一下 Tiptap 的相关概念。

Tiptap 模块

Tiptap采用 monorepo 的方式构建代码,相关的包都在 packages 下:

富文本编辑器Tiptap系列教程——Tiptap模块&概念详解

我们上篇文章初始化安装时有以下几个模块:

  • @tiptap/vue-3:适用于 vue3 的 tiptap 组件,类似的还有@tiptap/react@tiptap/vue-2

    Tiptap 简化了创建 ProseMirror 编辑器的大部分繁重工作,例如创建 EditorView、设置初始 EditorState 等。

    在 vue3 中可以很方便的使用useEditor钩子进行初始化,vue2 可以使用new Editor初始化。

    我们可以在项目中通过继承 Tiptap 的Editor,并在其基础上封装了一些基础设置和常用方法。

  • @tiptap/pm:提供了所有重要的 ProseMirror 包,如 prosemirror-state、prosemirror-view 或 prosemirror-model,这样我们就可以访问所有 ProseMirror 内部各种强大的 API,比如我们需要注册插件的时候:

    import { Plugin, PluginKey } from '@tiptap/pm/state'
    
    ...
    addProseMirrorPlugins() {
      return [
        new Plugin({
          key: new PluginKey('xxx'),
          ...
        })
      ]
    }
    ...
    
  • @tiptap/starter-kit:StarterKit 是最流行的 Tiptap 扩展的集合,包含最基本的 Tiptap 节点、标记和扩展,并且可以对其中的一个或多个进行配置或禁用,在项目中我们可以先引入 StarterKit 再根据自己的需求引入其他扩展即可。

    import StarterKit from '@tiptap/starter-kit'
    
    const editor = new Editor({
      ...
      extensions: [
        StarterKit.configure({
          // 禁用历史记录
          history: false,
    
          // 目录只有一级目录和二级目录
          heading: {
            levels: [1, 2],
          }
        }),
      ],
      ...
    })
    

Tiptap 初始化配置

我们在初始化编辑器的时候可以为编辑器开启一些默认配置,如:初始内容 content、扩展 extensions、自动获取焦点 autofocus、是否可编辑 editable 等。

import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'

new Editor({
  extensions: [Document, Paragraph, Text],
  content: `
    <h2>
      Hi there,
    </h2>
  `,
  autofocus: false,
  editable: true,
})

我们来看下所有可用的配置项列表:

element

将编辑器内容绑定到指定的元素,针对于 JS 引用,vue 中不用配置,使用EditorComponent即可。

extensions

Tiptap 扩展列表,可以使用 StarterKit 默认扩展,或其他扩展。

import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Highlight from '@tiptap/extension-highlight'

new Editor({
  // 使用默认扩展
  extensions: [StarterKit],

  // 使用其他扩展
  extensions: [Document, Paragraph, Text],

  // 也可以混着用
  extensions: [StarterKit, Highlight],
})

content

初始化时传递给编辑器的内容,可以是 HTML 或 JSON 格式。

editable

设置编辑器读写权限,true 可编辑 false 只读。

autofocus

设置编辑器是否自动聚焦和设置光标位置:

  • ‘start’: 设置光标在编辑器文档的最前面
  • ‘end’: 设置光标在编辑器文档的最后面
  • ‘all’ 选中全部文档
  • Number: 光标设置到文档中的特定位置
  • true: 自动获取焦点
  • false: 禁用自动获取焦点
  • null: 禁用自动获取焦点

enableInputRules

默认情况下开启所有输入规则,通过enableInputRules可以自定义输入规则。一般不会直接在这里设置,而是在节点或扩展中通过addInputRules来设置。

比如:我们使用 markdown 语法的规则来定义有序无序列表的输入,在输入-+*后空格启用无序列表,输入1a后输入.启用有序列表。

export const List = Node.create({
  name: 'list',
  // ...
  // 有序无序列表输入规则
  addInputRules() {
    return [
      new InputRule({
        find: /^\s*([-+*])\s$/,
        handler({ range, chain, state }) {
          if (isInListNde(state)) return
          chain().deleteRange(range).wrapInBulletList().run()
        },
      }),
      new InputRule({
        find: /^\s*([1a一][.、])\s$/,
        handler({ range, chain, match, state }) {
          if (isInListNde(state)) return
          if (match[0]?.startsWith('a')) {
            chain()
              .deleteRange(range)
              .wrapInOrderedList()
              .toggleListStyle({ listStyle: '4' })
              .run()
          } else if (match[0]?.startsWith('一')) {
            chain()
              .deleteRange(range)
              .wrapInOrderedList()
              .toggleListStyle({ listStyle: '7' })
              .run()
          } else {
            chain().deleteRange(range).wrapInOrderedList().run()
          }
        },
      }),
    ]
  },
})

enablePasteRules

默认情况下开启所有粘贴规则,通过enablePasteRules可以自定义粘贴规则。

injectCSS

Tiptap 默认注入的css 样式,通过设置injectCSS可以禁用掉。

injectNonce

HTML nonce是一种告诉浏览器特定脚本或样式元素的内联内容不是由某些(恶意)第三方注入到文档中,而是由控制文档的服务器的人故意放入文档中的方法。

使用injectNonce可以指定要添加到动态创建的元素的 nonce,如设置injectNonce: "your-nonce-here",这样在控制台中看到的样式文件会带有nonce标志。

富文本编辑器Tiptap系列教程——Tiptap模块&概念详解

editorProps

传递editorPropsProseMirror处理,用来覆盖编辑器事件或更改编辑器 DOM 元素属性。

parseOptions

传递parseOptionsProseMirror处理。

所有配置选项

NNde、Mark、Extension

Tiptap 的大多功能依赖于 节点 node标记 mark扩展 extension 这三个模块,所以想要敲开 Tiptap 的大门🚪,我们需要熟悉这三个模块。

节点 node

如果您将文档视为一棵树,那么节点就是该树中的一种内容。类似 DOM 树和 DOM 节点, Tiptap 节点指的是段落 Paragraph、标题 Heading、代码块 CodeBlock、表情 Emoji 等等。

前面讲到我们可以使用 StarterKit 来初始化节点、标记和扩展,我们看下 StarterKit 已经包含的 node 列表

富文本编辑器Tiptap系列教程——Tiptap模块&概念详解

像其他的一些比如图片 Image、表格 Table 等,需要导入如@tiptap/extension-image相关插件来配置。

npm install @tiptap/extension-image
new Editor({
  extensions: [Image],
})

Tiptap 已经为我们完成了大多数的 node,当然如果你觉得不够用或不好用的话,也可以自定义节点。

import { Node } from '@tiptap/core'

const CustomNode = Node.create({
  // Your code here
})

const editor = new Editor({
  extensions: [
    CustomNode,
    Document,
    Paragraph,
    Text,
  ],
})

标记 mark

富文本编辑器Tiptap系列教程——Tiptap模块&概念详解
如上图,当我们给节点设置加粗斜体后,在 html 内容中可以看到会讲<strong><em>标签添加到对应节点上,这些对应的 html 标签就是 mark。

可以将一个或多个标记应用于节点,例如添加内联格式(如粗体和斜体)或其他附加信息。

StarterKit 中已经包含了一些常用的 mark,其余的只需要引入即可,当然像 node 一样 mark 也是可以自定义的。

扩展 extension

Tiptap 提供具有更多功能的扩展,横向增加 Tiptap 的功能,像之前的 node 和 mark 都可以作为扩展添加到 Tiptap 中。

可以在社区中找到更多的 Tiptap 扩展

extension 的工作原理

尽管 Tiptap 试图隐藏 ProseMirror 的大部分复杂性,但它构建在 ProseMirror 之上,所以扩展的底层依然是基于 ProseMirror,要想使用自定义扩展这样的高级功能,必须对 ProseMirror 的原理有一定的了解。

现有的节点、标记和扩展都有 Github 地址,这样我们就能很方便的看到它们的源码。阅读官方扩展的源码我们可能会遇到 ProseMirror 的各种 API,然后可以在ProseMirror API中找到它,去了解并学习最终运用在自己的自定义扩展中。

自定义 extension

自定义扩展只需要通过 Extension.create 去创建一个扩展,最后引入到初始化 Editor 的 extensions 中即可。

import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create({
  // Your code here
})

const editor = new Editor({
  extensions: [
    // Register your custom extension with the editor.
    CustomExtension,
    // … and don't forget all other extensions.
    Document,
    Paragraph,
    Text,
    // …
  ],
})

最后

通过本文的介绍,想必大家已经对Tiptap重要的概念有了一定的了解,也知道了如何自定义节点或扩展。

推荐大家阅读一些比较简单的扩展源码,类似BoldCharacterCount这种,学习别人是怎么完成一个扩展,怎样使用Tiptap 或ProseMirror API 的,学了就会有收获💪。

以上就是本文的全部内容,希望这篇文章对你有所帮助,欢迎点赞和收藏 🙏,如果发现有什么错误或者更好的解决方案及建议,欢迎随时联系。

原文链接:https://juejin.cn/post/7246056370625413175 作者:翔子丶

(2)
上一篇 2023年6月19日 上午10:00
下一篇 2023年6月19日 上午10:11

相关推荐

发表回复

登录后才能评论