探索 Tiptap V3 的最新功能

Node API

Tiptap 的强大之处在于它的灵活性。你可以从零开始创建自己的扩展,构建量身定制的独特编辑体验。

创建一个节点

节点是你编辑器的基本构建块。它们可以是块节点或行内节点。好的学习实例包括 ParagraphHeadingCodeBlock

它们扩展了 扩展 API 的所有选项和方法,并添加了一些特定于节点的选项。

让我们添加一个简单的节点扩展,看看它是如何工作的。

import { Node } from '@tiptap/core'

const CustomNode = Node.create({
  name: 'customNode',

  addOptions() {
    return {
      HTMLAttributes: {},
    }
  },

  parseHTML() {
    return [
      {
        tag: 'div',
      },
    ]
  },

  renderHTML({ HTMLAttributes }) {
    return ['div', HTMLAttributes, 0]
  },
})

你也可以使用回调函数来创建节点。如果你想封装扩展的逻辑,比如定义事件处理程序或其他自定义逻辑,这非常有用。

import { Node } from '@tiptap/core'

const CustomNode = Node.create(() => {
  // 你可以在这里定义变量或函数,用于你的架构定义
  const customVariable = 'foo'

  function onCreate() {}
  function onUpdate() {}

  return {
    name: 'customNode',
    onCreate,
    onUpdate,

    // 你的代码写在这里。
  }
})

这段代码创建了一个名为 CustomNode 的新节点扩展。它添加了一个 addOptions 方法来定义节点的选项,这些选项可由用户配置。同时它还添加了 parseHTMLrenderHTML 方法,定义节点如何解析和渲染为 HTML。

安装到编辑器时,与其他扩展一样,将其添加到 extensions 数组中。

import { Editor } from '@tiptap/core'

new Editor({
  extensions: [CustomNode],
})

// 或者如果使用 React 或 Vue

const editor = useEditor({
  extensions: [CustomNode],
})

现在让我们仔细看看可用于节点的选项和方法。

节点选项

创建一个节点时,可以定义可由用户配置的选项。这些选项可用于自定义节点的行为或外观。

parseHTML

parseHTML 方法用于定义如何从 HTML 中解析标记。它应该返回一个数组,表示标记的属性。

将映射到 ProseMirror 架构 中的 parseDOM 属性。

const CustomMark = Mark.create({
  name: 'customMark',

  parseHTML() {
    return [
      {
        tag: 'span',
        getAttrs: (node) => {
          return {
            class: node.getAttribute('class'),
          }
        },
      },
    ]
  },
})

这将在粘贴事件期间用于将 HTML 内容解析为标记。

renderHTML

renderHTML 方法用于定义如何将标记渲染为 HTML。它应该返回一个表示标记 HTML 表示的数组。

将映射到 ProseMirror 架构 中的 toDOM 属性。

const CustomMark = Mark.create({
  name: 'customMark',

  renderHTML({ HTMLAttributes }) {
    return ['span', HTMLAttributes, 0]
  },
})

这将在复制事件期间用于将标记渲染为 HTML。更多细节请参见 扩展现有扩展 指南。

addAttributes

addAttributes 方法用于定义标记的自定义属性。它应该返回一个包含属性名称及其默认值的对象。

将映射到 ProseMirror 架构 中的 attrs 属性。

const CustomMark = Mark.create({
  name: 'customMark',

  addAttributes() {
    return {
      customAttribute: {
        default: 'value',
        parseHTML: (element) => element.getAttribute('data-custom-attribute'),
      },
    }
  },
})

有关更多细节,请参见 扩展现有扩展 指南。

topNode

定义此节点是否应为顶级节点(文档)。

将映射到 ProseMirror 架构 中的 topNode 属性。

const CustomNode = Node.create({
  name: 'customNode',

  topNode: true,
})

content

该节点的内容表达式,如 架构指南 所述。如果未给定,则该节点不允许任何内容。

你可以在 Prosemirror 文档中阅读更多内容 这里

const CustomNode = Node.create({
  name: 'customNode',

  content: 'block+',
})

marks

该节点内允许的标记。可以是指向标记名称或组的空格分隔字符串,"_" 明确允许所有标记,或 "" 禁止标记。如果未给定,具有行内内容的节点默认允许所有标记,其他节点默认不允许标记。

将映射到 ProseMirror 架构 中的 marks 属性。

const CustomNode = Node.create({
  name: 'customNode',

  marks: 'strong em',
})

group

该节点所属的组或用空格分隔的组,可以在架构的内容表达式中引用。

默认情况下,Tiptap 将 blockinline 作为节点的组。如果你想将特定节点分组并在架构中处理它们,也可以使用自定义组。

将映射到 ProseMirror 架构 中的 group 属性。

const CustomNode = Node.create({
  name: 'customNode',

  group: 'block',
})

inline

此属性应该设置为 true ,以表示行内节点。(对文本节点是隐含的)。

将映射到 ProseMirror 架构 中的 inline 属性。

const CustomNode = Node.create({
  name: 'customNode',

  inline: true,
})

atom

可以设置为 true,以指示虽然这不是一个 叶节点,但它没有直接可编辑的内容,应视为视图中的一个单元。

将映射到 ProseMirror 架构 中的 atom 属性。

const CustomNode = Node.create({
  name: 'customNode',

  atom: true,
})

selectable

控制该类型的节点是否可以被选中作为 节点选择。默认情况下,对于非文本节点为 true。

将映射到 ProseMirror 架构 中的 selectable 属性。

const CustomNode = Node.create({
  name: 'customNode',

  selectable: false,
})

draggable

确定该类型的节点是否可以在不被选中的情况下拖动。默认值为 false。

将映射到 ProseMirror 架构 中的 draggable 属性。

const CustomNode = Node.create({
  name: 'customNode',

  draggable: true,
})

code

可以用于指示此节点包含代码,这会导致某些命令表现得不同。

将映射到 ProseMirror 架构 中的 code 属性。

const CustomNode = Node.create({
  name: 'customNode',

  code: true,
})

whitespace

控制该节点中空格的解析方式。默认值为 "normal",这会导致 DOM 解析器 在正常模式下折叠空格,其他情况则标准化(用空格替换换行等)。"pre" 则使解析器保留节点内的空格。如果未给定此选项,但 code 为 true,则 whitespace 默认值为 "pre"

将映射到 ProseMirror 架构 中的 whitespace 属性。

const CustomNode = Node.create({
  name: 'customNode',

  whitespace: 'pre',
})

linebreakReplacement

允许将 单个 节点设置为换行等价物(例如 hardBreak)。在转换具有空格设置为 "pre" 并且不支持换行节点(例如 codeBlock)与支持换行节点的其他块类型(例如段落)之间进行转换时,将使用此节点作为换行,而不是删除换行符。

将映射到 ProseMirror 架构 中的 linebreakReplacement 属性。

const CustomNode = Node.create({
  name: 'customNode',

  linebreakReplacement: true,
})

defining

启用时,同时启用 definingAsContextdefiningForContent

将映射到 ProseMirror 架构 中的 defining 属性。

const CustomNode = Node.create({
  name: 'customNode',

  defining: true,
})

isolating

启用时(默认值为 false),该类型节点的两侧视为边界,常规编辑操作(如退格或提升)不会穿越。一个应该启用此功能的节点示例是表格单元。

将映射到 ProseMirror 架构 中的 isolating 属性。

const CustomNode = Node.create({
  name: 'customNode',

  isolating: true,
})

addNodeView(高级)

对于高级用例,比如需要在节点内部执行 JavaScript,例如渲染一个围绕图像的复杂界面,你需要了解节点视图。

它们非常强大,但也很复杂。简单来说,你需要返回一个父 DOM 元素和一个内容应该渲染到的 DOM 元素。查看以下简化示例:

import Image from '@tiptap/extension-image'

const CustomImage = Image.extend({
  addNodeView() {
    return () => {
      const container = document.createElement('div')

      container.addEventListener('click', (event) => {
        alert('点击了容器')
      })

      const content = document.createElement('div')
      container.append(content)

      return {
        dom: container,
        contentDOM: content,
      }
    }
  },
})

关于节点视图有很多内容需要学习,因此请前往我们指南中关于节点视图的 专门部分 获取更多信息。如果你正在寻找一个实际的例子,查看 TaskItem 节点的源代码。这个节点使用节点视图来渲染复选框。