---
title: "编辑器命令"
description: "了解 Tiptap 中的命令执行和链式调用。发现如何在编辑器命令文档中扩展功能。"
canonical_url: "https://tiptap.zhcndoc.com/editor/api/commands"
---

# 编辑器命令

了解 Tiptap 中的命令执行和链式调用。发现如何在编辑器命令文档中扩展功能。

编辑器提供了大量命令，用于以编程方式添加或更改内容或改变选择。如果你想构建自己的编辑器，您绝对想要了解更多关于它们的信息。

## 执行命令

所有可用的命令通过编辑器实例访问。假设你想让用户点击按钮时文本变为粗体。实现方法如下：

```js
editor.commands.setBold()
```

虽然这样完全可以将选定文本设置为粗体，但你可能希望在一次运行中链接多个命令。让我们看看这是如何工作的。

### 链接命令

大多数命令可以组合为一个调用。在大多数情况下，这比分别调用函数要短。以下是一个将选定文本变为粗体的示例：

```js
editor.chain().focus().toggleBold().run()
```

`.chain()` 是启动新链所必需的，而 `.run()` 是实际执行所有中间命令所必需的。

在上面的示例中，同时执行了两个不同的命令。当用户点击内容外部的按钮时，编辑器不再处于焦点状态。这就是为什么你可能希望在大多数命令中添加 `.focus()` 调用。它将焦点返回给编辑器，以便用户能够继续输入。

所有链式命令都是排队的。它们组合成一个单一的事务。这意味着内容仅更新一次，`update` 事件也仅触发一次。

例如，你可能希望将一个 **delete** 和一个 **insert** 命令链接在一起：

```js
addCommands() {
  return {
    delete: () => ({ tr }) => {
      tr.delete(tr.selection.from, tr.selection.to)

      return true
    },
    insert: (content: string) => ({ tr }) => {
      tr.insertText(content, tr.selection.from)

      return true
    },
  }
}
```

现在你可以将这两个命令链接起来：

```js
editor.chain().delete().insert('foo').run()
```

#### 在自定义命令中链式调用

在链式调用命令时，事务将被暂时保留。如果你想在自定义命令中链接命令，你需要使用上述事务并进行添加。以下是如何做到这一点：

```js
addCommands() {
  return {
    customCommand: attributes => ({ chain }) => {
      // 不起作用：
      // return editor.chain() …

      // 起作用：
      return chain()
        .insertContent('foo!')
        .insertContent('bar!')
        .run()
    },
  }
}
```

### 行内命令

在某些情况下，在命令中加入更多逻辑是有帮助的。这就是为什么你可以在命令中执行命令。我知道，这听起来很疯狂，但我们来看看一个示例：

```js
editor
  .chain()
  .focus()
  .command(({ tr }) => {
    // 操作事务
    tr.insertText('嘿，这太酷了!')

    return true
  })
  .run()
```

### 干跑命令

有时候，你并不想实际运行命令，而只是想知道是否可以运行命令，例如在菜单中显示或隐藏按钮。这就是我们添加 `.can()` 的原因。所有在此方法之后的内容将被执行，而不对文档应用更改：

```js
editor.can().toggleBold()
```

你也可以将其与 `.chain()` 一起使用。以下是一个例子，检查是否可以应用所有命令：

```js
editor.can().chain().toggleBold().toggleItalic().run()
```

如果可以应用命令，则两个调用都会返回 `true`，否则返回 `false`。

为了使其在你的自定义命令中工作，别忘了返回 `true` 或 `false`。

对于一些你自己的命令，你可能想使用原始的 [事务](https://tiptap.zhcndoc.com/editor/core-concepts/introduction.md)。为了使它们与 `.can()` 一起工作，你应该检查事务是否应该被派遣。以下是如何创建一个简单的 `.insertText()` 命令：

```js
export default (value) =>
  ({ tr, dispatch }) => {
    if (dispatch) {
      tr.insertText(value)
    }

    return true
  }
```

如果你只是封装另一个 Tiptap 命令，你不需要检查，我们会为你做到。

```js
addCommands() {
  return {
    bold: () => ({ commands }) => {
      return commands.toggleMark('bold')
    },
  }
}
```

如果你只是封装一个普通的 ProseMirror 命令，你仍然需要传递 `dispatch`。这时也不需要进行检查：

```js
import { exitCode } from '@tiptap/pm/commands'

export default () =>
  ({ state, dispatch }) => {
    return exitCode(state, dispatch)
  }
```

### 尝试命令

如果你想运行一系列命令，但只想应用第一个成功的命令，你可以使用 `.first()` 方法。该方法依次运行一个命令，直到遇到第一个返回 `true` 的命令为止。

例如，退格键首先尝试撤消输入规则。如果成功，它就会停止。如果没有应用输入规则，因此无法撤消，它将运行下一个命令并删除所选内容（如果有的话）。以下是简化的示例：

```js
editor.commands.first(({ commands }) => [
  () => commands.undoInputRule(),
  () => commands.deleteSelection(),
  // …
])
```

在命令内部，你可以执行相同的操作：

```js
export default () =>
  ({ commands }) => {
    return commands.first([
      () => commands.undoInputRule(),
      () => commands.deleteSelection(),
      // …
    ])
  }
```

## 命令列表

请查看下面列出的所有核心命令。它们应该给你一个关于可能性的良好第一印象。

### 内容

| 命令                  | 描述                     |
| ------------------- | ---------------------- |
| `clearContent()`    | 清空整个文档。                |
| `insertContent()`   | 在当前位插入一个节点或 HTML 字符串。  |
| `insertContentAt()` | 在特定位置插入一个节点或 HTML 字符串。 |
| `setContent()`      | 用新内容替换整个文档。            |

### 节点和标记

| 命令                      | 描述                    |
| ----------------------- | --------------------- |
| `clearNodes()`          | 将节点标准化为简简单单的段落。       |
| `createParagraphNear()` | 创建一个邻近的段落。            |
| `deleteNode()`          | 删除一个节点。               |
| `extendMarkRange()`     | 扩展文本选择至当前标记。          |
| `exitCode()`            | 退出代码块。                |
| `joinBackward()`        | 将两个节点向后连接。            |
| `joinForward()`         | 将两个节点向前连接。            |
| `lift()`                | 移除现有包裹。               |
| `liftEmptyBlock()`      | 如果为空，则提升块。            |
| `newlineInCode()`       | 在代码中添加换行符。            |
| `resetAttributes()`     | 将某些节点或标记的属性重置为默认值。    |
| `setMark()`             | 添加具有新属性的标记。           |
| `setNode()`             | 用节点替换给定范围。            |
| `splitBlock()`          | 从现有节点分叉出一个新节点。        |
| `toggleMark()`          | 切换标记的开关。              |
| `toggleNode()`          | 用另一个节点切换节点。           |
| `toggleWrap()`          | 在另一个节点中包裹节点，或移除现有的包裹。 |
| `undoInputRule()`       | 撤消输入规则。               |
| `unsetAllMarks()`       | 删除当前选择中的所有标记。         |
| `unsetMark()`           | 删除当前选择中的标记。           |
| `updateAttributes()`    | 更新节点或标记的属性。           |

### 列表

| 命令                | 描述              |
| ----------------- | --------------- |
| `liftListItem()`  | 提升列表项到一个包装列表中。  |
| `sinkListItem()`  | 将列表项下沉到一个内部列表中。 |
| `splitListItem()` | 将一个列表项拆分为两个列表项。 |
| `toggleList()`    | 在不同类型的列表之间切换。   |
| `wrapInList()`    | 将节点包裹在一个列表中。    |

### 选择

| 命令                     | 描述                  |
| ---------------------- | ------------------- |
| `blur()`               | 从编辑器中移除焦点。          |
| `deleteRange()`        | 删除给定范围。             |
| `deleteSelection()`    | 删除选定内容（如果有的话）。      |
| `enter()`              | 触发回车。               |
| `focus()`              | 在给定位置将焦点放在编辑器上。     |
| `keyboardShortcut()`   | 触发键盘快捷键。            |
| `scrollIntoView()`     | 将选定内容滚动到可视区域。       |
| `selectAll()`          | 选择整个文档。             |
| `selectNodeBackward()` | 向后选择一个节点。           |
| `selectNodeForward()`  | 向前选择一个节点。           |
| `selectParentNode()`   | 选择父节点。              |
| `setNodeSelection()`   | 创建一个 NodeSelection。 |
| `setTextSelection()`   | 创建一个 TextSelection。 |

## 编写自己的命令

所有扩展都可以添加额外的命令（大多数都有），请查看提供的节点的具体 [文档](https://tiptap.zhcndoc.com/editor/extensions/nodes.md)、[标记](https://tiptap.zhcndoc.com/editor/extensions/marks.md)、和 [功能](https://tiptap.zhcndoc.com/editor/extensions/functionality.md) 来了解更多信息。 当然，您还可以 [添加自定义扩展](https://tiptap.zhcndoc.com/editor/extensions/custom-extensions.md) 和自定义命令。
那么如何编写这些命令呢？关于这方面还有一些内容需要学习。
