---
title: "扩展 API"
description: "为您的 Tiptap 编辑器创建一个新的扩展，从零开始构建独特的编辑器体验。在文档中了解更多信息！"
canonical_url: "https://tiptap.zhcndoc.com/editor/extensions/custom-extensions/create-new/extension"
---

# 扩展 API

为您的 Tiptap 编辑器创建一个新的扩展，从零开始构建独特的编辑器体验。在文档中了解更多信息！

Tiptap 的强大之处在于其灵活性。您可以从头开始创建自己的扩展，并构建一个独特的编辑器体验，以满足您的需求。

所有扩展的基本结构都是相同的，无论您是创建节点、标记还是功能更改。而且，Tiptap 中的一切都是基于扩展的。

## 创建扩展

扩展为 Tiptap 添加了新功能。在文档中，您会经常看到“扩展”这个词，甚至在节点和标记中也是如此。但也有字面意义上的扩展。这些扩展不能添加到架构中（就像标记和节点那样），但它们可以添加功能或改变编辑器的行为。

一个很好的例子是监听编辑器事件并处理它们的内容。像这样：

```typescript
import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create({
  name: 'customExtension',

  onUpdate() {
    console.log(this.editor.getJSON())
  },
})
```

您也可以使用回调函数创建扩展。这在您想封装扩展逻辑时非常有用，例如，当您想定义事件处理程序或其他自定义逻辑时。

```js
import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create(() => {
  // 定义扩展内部使用的变量或函数
  const customVariable = 'foo'

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

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

    // 在这里编写您的代码。
  }
})
```

此扩展监听编辑器的 `update` 事件，并将编辑器当前的 JSON 表示日志输出到控制台。

它这样安装到编辑器中：

```typescript
import { Editor } from '@tiptap/core'

const editor = new Editor({
  extensions: [CustomExtension],
})

// 或者在使用 React 或 Vue 时

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

此扩展数组可以包含任意数量的扩展。它们将按列出的顺序安装，或按它们的 `priority` 属性排序。

现在我们已经看到扩展的基本结构，让我们深入了解您可以用来创建自己扩展的所有扩展选项。

## 扩展选项

在创建扩展时，您可以定义一组用户可配置的选项。这些选项可以用于自定义扩展的行为，或提供额外的功能。

### `name`

扩展的名称。这用于在编辑器的扩展管理器中识别扩展。

```typescript
const CustomExtension = Extension.create({
  name: 'customExtension',
})
```

如果扩展是节点或标记，则名称用于在编辑器的架构中识别节点或标记，并因此保存在编辑器内容的 JSON 表示中。有关更多信息，请参见 [存储您的内容为 JSON](https://tiptap.zhcndoc.com/guides/output-json-html.md#option-1-json)。

### `priority`

优先级定义扩展注册的顺序。默认优先级为 `100`，大多数扩展都是这个值。具有更高优先级的扩展将更早加载。

```js
import Link from '@tiptap/extension-link'

const CustomLink = Link.extend({
  priority: 1000,
})
```

扩展加载的顺序影响两个方面：

1. 插件顺序（优先级更高的扩展的 ProseMirror 插件将先运行。）
2. 架构顺序

例如，[`Link`](https://tiptap.zhcndoc.com/editor/extensions/marks/link.md) 标记具有更高的优先级，这意味着它将被渲染为 `<a href="…"><strong>Example</strong></a>` 而不是 `<strong><a href="…">Example</a></strong>`。

### `addOptions`

`addOptions` 方法用于定义扩展的选项。此方法应返回一个对象，其中包含用户可配置的选项。

```typescript
type CustomExtensionOptions = {
  customOption: string
}

declare module '@tiptap/core' {
  interface ExtensionOptions {
    customOption: CustomExtensionOptions
  }
}

const CustomExtension = Extension.create<CustomExtensionOptions>({
  name: 'customExtension',

  addOptions() {
    return {
      customOption: 'default value',
    }
  },
})
```

这会暴露在安装扩展时可以设置的配置：

```typescript
const editor = new Editor({
  extensions: [CustomExtension.configure({ customOption: 'new value' })],
})
```

### `addStorage`

`addStorage` 方法用于定义扩展的存储（本质上是一个简单的状态管理器）。此方法应返回一个对象，其中包含扩展可以使用的存储。

```typescript
type CustomExtensionStorage = {
  customValue: string
}

declare module '@tiptap/core' {
  interface ExtensionStorage {
    customExtension: CustomExtensionStorage
  }
}

const CustomExtension = Extension.create<any, CustomExtensionStorage>({
  name: 'customExtension',

  addStorage() {
    return {
      customValue: 'default value',
    }
  },
})
```

这会暴露在扩展内部可以访问的存储：

```typescript
const CustomExtension = Extension.create<any, CustomExtensionStorage>({
  name: 'customExtension',

  addStorage() {
    return {
      customValue: 'default value',
    }
  },

  onUpdate() {
    console.log(this.storage.customValue) // 'default value'
  },
})
```

或者通过编辑器：

```typescript
const editor = new Editor({
  extensions: [CustomExtension],
})

editor.storage.customExtension.customValue // 'default value'
```

> **注意:**
>
> `editor.storage` 由扩展的名称命名空间。

### `addCommands`

`addCommands` 方法用于定义扩展的命令。此方法应返回一个对象，其中包含用户可以执行的命令。

```typescript
declare module '@tiptap/core' {
  interface Commands<ReturnType> {
    customExtension: {
      customCommand: () => ReturnType
    }
  }
}

const CustomExtension = Extension.create({
  name: 'customExtension',

  addCommands() {
    return {
      customCommand:
        () =>
        ({ commands }) => {
          return commands.setContent('Custom command executed')
        },
    }
  },
})
```

> **在 addCommands 内部使用 commands 参数:**
>
> 要在 `addCommands` 内部访问其他命令，请使用传递给它的 `commands` 参数。

这会暴露用户可以执行的命令：

```typescript
const editor = new Editor({
  extensions: [CustomExtension],
})

editor.commands.customCommand() // 'Custom command executed'
editor.chain().customCommand().run() // 'Custom command executed'
```

### `addKeyboardShortcuts`

`addKeyboardShortcuts` 方法用于定义键盘快捷键。此方法应返回一个对象，其中包含用户可以使用的键盘快捷键。

```typescript
const CustomExtension = Extension.create({
  name: 'customExtension',

  addKeyboardShortcuts() {
    return {
      'Mod-k': () => {
        console.log('Keyboard shortcut executed')
      },
    }
  },
})
```

这会暴露可供用户使用的键盘快捷键。

### `addInputRules`

使用输入规则，您可以定义正则表达式以监听用户输入。它们用于 Markdown 快捷方式，例如将文本 `(c)` 转换为 `©`（还有许多其他用途）与 [`Typography`](https://tiptap.zhcndoc.com/editor/extensions/functionality/typography.md) 扩展一起使用。对于标记，使用 `markInputRule` 辅助函数，对于节点，使用 `nodeInputRule`。

默认情况下，位于两侧的波浪号之间的文本会转换为 ~~删除线文本~~。如果您认为在每边只需一个波浪号就足够了，可以像这样覆盖输入规则：

```typescript
import { Extension } from '@tiptap/core'
import { markInputRule } from '@tiptap/core'

const CustomExtension = Extension.create({
  name: 'customExtension',

  addInputRules() {
    return [
      markInputRule({
        find: /(?:~)((?:[^~]+))(?:~)$/,
        type: this.editor.schema.marks.strike,
      }),
    ]
  },
})
```

现在，当您输入 `~striked text~` 时，它将被转换为 ~~删除线文本~~。

想了解更多关于输入规则的信息？请查看 [输入规则](https://tiptap.zhcndoc.com/editor/api/input-rules.md) 文档。

### `addPasteRules`

粘贴规则的工作方式与输入规则（见上文）相似。但不同之处在于，它们应用于粘贴的内容，而不是监听用户输入。

正则表达式有一个微小的区别。输入规则通常以 `$` 符号结束（这意味着“在行尾断言位置”），而粘贴规则通常浏览所有内容，不带该 `$` 符号。

将上述示例应用于粘贴规则将如下所示：

```typescript
import { Extension } from '@tiptap/core'
import { markPasteRule } from '@tiptap/core'

const CustomExtension = Extension.create({
  name: 'customExtension',

  addPasteRules() {
    return [
      markPasteRule({
        find: /(?:~)((?:[^~]+))(?:~)/g,
        type: this.editor.schema.marks.strike,
      }),
    ]
  },
})
```

想了解更多关于粘贴规则的信息？请查看 [粘贴规则](https://tiptap.zhcndoc.com/editor/api/paste-rules.md) 文档。

### 事件

您甚至可以将您的 [事件监听器](https://tiptap.zhcndoc.com/editor/api/events.md) 移动到一个单独的扩展。以下是一个涉及所有事件的示例：

```typescript
import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create({
  onBeforeCreate() {
    // 编辑器即将被创建。
  },
  onCreate() {
    // 编辑器已准备就绪。
  },
  onUpdate() {
    // 内容已更改。
  },
  onSelectionUpdate({ editor }) {
    // 选择已更改。
  },
  onTransaction({ transaction }) {
    // 编辑器状态已更改。
  },
  onFocus({ event }) {
    // 编辑器已获得焦点。
  },
  onBlur({ event }) {
    // 编辑器不再获得焦点。
  },
  onDestroy() {
    // 编辑器正在被销毁。
  },
})
```

### `dispatchTransaction`

该钩子允许您在事务分发之前拦截和修改事务。它采用类似 Koa 或 Express 的**中间件模式**，每个扩展都会接收到 `transaction` 和 `next` 函数。

#### 工作原理

1. **中间件链**：当事务被分发时，Tiptap 会创建一个包含所有定义了 `dispatchTransaction` 钩子的扩展的链。
2. **按优先级排序**：该链按[扩展优先级](#priority)排序。具有**更高优先级**（如 `1000`）的扩展会**更早调用**，并且包装优先级较低的扩展。
3. **传递调用**：您必须调用 `next(transaction)` 才能将事务传递给链中的下一个扩展。最终，最后的 `next()` 调用会将事务传递给编辑器的基础分发函数（或者如果定义了自定义的 `editorProps.dispatchTransaction`，则调用自定义函数）。
4. **阻止事务**：如果您不调用 `next(transaction)`，事务将被阻止，不会传递给编辑器。

```typescript
import { Extension } from '@tiptap/core'

const LoggingExtension = Extension.create({
  name: 'loggingExtension',
  priority: 1000, // 提前运行

  dispatchTransaction({ transaction, next }) {
    console.log('在其他扩展之前拦截事务...')
    
    // 您可以在此修改事务
    // transaction.setMeta('customMeta', true)

    // 调用 next 将其传递给下一个扩展
    next(transaction)
    
    console.log('事务已由后续链处理。')
  },
})
```

#### 中间件生命周期

因为每个扩展都会“包裹”下一个扩展，您可以在事务被后续链和编辑器处理之前和之后执行代码。

```typescript
dispatchTransaction({ transaction, next }) {
  // 1. 预处理：在调用 next() 之前执行的代码
  const start = performance.now()

  next(transaction)

  // 2. 后处理：在调用 next() 之后执行的代码
  const end = performance.now()
  console.log(`分发耗时 ${end - start} 毫秒`)
}
```

### `addProseMirrorPlugins`

您可以向扩展添加 ProseMirror 插件。如果您想使用 ProseMirror 插件扩展编辑器，这非常有用。

#### 使用现有 ProseMirror 插件

您可以将现有的 ProseMirror 插件包装在 Tiptap 扩展中，如下面的示例所示。

```typescript
import { history } from '@tiptap/pm/history'

const History = Extension.create({
  addProseMirrorPlugins() {
    return [
      history(),
      // …
    ]
  },
})
```

#### 创建 ProseMirror 插件

您也可以创建自定义的 ProseMirror 插件。以下是一个将消息记录到控制台的自定义 ProseMirror 插件示例。

```typescript
import { Plugin, PluginKey } from '@tiptap/pm/state'
import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create({
  name: 'customExtension',

  addProseMirrorPlugins() {
    return [
      new Plugin({
        key: new PluginKey('customPlugin'),
        view() {
          return {
            update() {
              console.log('自定义插件已更新')
            },
          }
        },
      }),
    ]
  },
})
```

要了解有关 ProseMirror 插件的更多信息，请查阅 [ProseMirror 文档](https://prosemirror.net/docs/ref/#state.Plugin)。

### `addGlobalAttributes`

`addGlobalAttributes` 方法用于为多个扩展同时添加属性。这对于诸如文本对齐、行高或其他应在许多节点和标记类型上可用的样式相关属性非常有用。

您可以通过 `types` 属性指定哪些扩展将接收该属性：

- `types: ['heading', 'paragraph']` - 应用于特定类型名称
- `types: '*'` - 应用于所有节点类型（不包括内置的文本节点）和所有标记类型
- `types: 'nodes'` - 应用于所有节点类型（不包括内置的文本节点）
- `types: 'marks'` - 应用于所有标记类型

以下示例演示了如何将属性应用于特定类型：

```typescript
const TextAlign = Extension.create({
  name: 'textAlign',

  addGlobalAttributes() {
    return [
      {
        types: ['heading', 'paragraph'],
        attributes: {
          textAlign: {
            default: 'left',
            renderHTML: (attributes) => ({
              style: `text-align: ${attributes.textAlign}`,
            }),
            parseHTML: (element) => element.style.textAlign || 'left',
          },
        },
      },
    ]
  },
})
```

更多选项（包括字符串简写语法）请参阅[应用全局属性](https://tiptap.zhcndoc.com/editor/extensions/custom-extensions/extend-existing.md#apply-global-attributes-to-all-extensions) 指南。

### `addExtensions`

您可以向扩展添加更多扩展。如果您想创建一组属于同一类别的扩展，这非常有用。

```typescript
import { Extension } from '@tiptap/core'
import CustomExtension1 from './CustomExtension1'

const CustomExtension = Extension.create({
  name: 'customExtension',

  addExtensions() {
    return [
      CustomExtension1.configure({
        name: 'customExtension1',
      }),
    ]
  },
})
```

### `extendNodeSchema`

您可以使用 `extendNodeSchema` 方法扩展编辑器的 NodeConfig。如果您想向节点架构添加额外属性，这将非常有用。

```typescript
import { Extension } from '@tiptap/core'

declare module '@tiptap/core' {
  // 这将向 NodeConfig 添加一个新的配置选项
  interface NodeConfig {
    customAttribute: {
      default: null
    }
  }
}

const CustomExtension = Extension.create({
  name: 'customExtension',

  extendNodeSchema() {
    return {
      customAttribute: {
        default: null,
      },
    }
  },
})
```

### `extendMarkSchema`

您可以使用 `extendMarkSchema` 方法扩展编辑器的 MarkConfig。如果您想向标记架构添加额外属性，这将非常有用。

```typescript
import { Extension } from '@tiptap/core'

declare module '@tiptap/core' {
  // 这将向 MarkConfig 添加一个新的配置选项
  interface MarkConfig {
    customAttribute: {
      default: null
    }
  }
}

const CustomExtension = Extension.create({
  name: 'customExtension',

  extendMarkSchema() {
    return {
      customAttribute: {
        default: null,
      },
    }
  },
})
```

## 这有什么可用的？

这些扩展不是类，但您在扩展中的 `this` 处仍然可以访问一些重要内容。

```js
// 扩展的名称，例如 'bulletList'
this.name

// 编辑器实例
this.editor

// ProseMirror 类型（如果是节点或标记）
this.type

// 包含所有设置的对象
this.options

// 所有扩展的内容
this.parent

// 存储对象
this.storage
```
