---
title: "集成快照"
description: "编辑器历史记录用于手动和自动版本管理您的文档。在这里了解如何设置和使用它！"
canonical_url: "https://tiptap.zhcndoc.com/collaboration/documents/snapshot"
---

# 集成快照

编辑器历史记录用于手动和自动版本管理您的文档。在这里了解如何设置和使用它！

文档历史记录会记录您内容的每一次更改，以便您能够撤销错误、审核编辑或从任意时刻创建新草稿。

本页将向您介绍 **历史** 扩展的安装、配置和常见任务。

- **1. 激活试用或订阅**

  在您的账户中开始免费 [试用](https://cloud.tiptap.dev/v2/billing) 或订阅 [Tiptap 计划](https://cloud.tiptap.dev/v2/billing)。
- **2. 启动文档服务器**

  在您的仪表板中 [添加环境](https://cloud.tiptap.dev/v2/configuration/document-server) 并配置您的 [文档服务器](https://cloud.tiptap.dev/v2/configuration/document-server)。
- **3. 从私有注册表安装**

  要安装此扩展，请通过按照 [设置指南](https://tiptap.zhcndoc.com/guides/pro-extensions.md) 访问 Tiptap 的私有 npm 注册表进行身份验证。

> **公共演示:**
>
> 编辑器内容在所有演示访问者之间共享。

> **Interactive demo:** [Snapshot](https://embed-pro.tiptap.dev/preview/Extensions/Snapshot)

## 访问专用注册表

版本历史扩展发布在 Tiptap 的私有 npm 注册表中。按 [私有注册表指南](https://tiptap.zhcndoc.com/guides/pro-extensions.md) 操作集成该扩展。如果您已经对您的 Tiptap 账户进行了身份验证，您可以直接跳转至 [#安装](#install)。

## 安装

```bash

npm install @tiptap-pro/extension-snapshot @hocuspocus/transformer

```

**注意**：
`@hocuspocus/transformer` 包用于将 Y.js 的二进制数据转换为 Tiptap JSON。同时也需要安装 Y.js 以支持协作。
如果尚未安装，请在项目中运行 `npm install yjs`。如果您使用 NPM，这应该会自动发生（因为它会自动解析对等依赖关系）。

## 设置

| 设置       | 类型                     | 默认值        |
| -------- | ---------------------- | ---------- |
| provider | `TiptapCollabProvider` | `null`     |
| onUpdate | `function`             | `() => {}` |

## 自动版本控制

自动版本控制功能会在固定间隔自动创建文档的新版本。这确保您拥有全面的更改历史，而无需手动干预。

您可以使用 `toggleVersioning` 命令切换此功能（默认：禁用）。

当您启用自动版本控制时，Tiptap 会在固定间隔（默认30秒，仅在文档发生更改时）创建新版本。这可能会创建许多版本，因此您可能想要增加间隔。
要自定义间隔，可以执行以下操作：

```typescript
// 设置自动版本间隔（以秒为单位）
const ydoc = provider.configuration.document
ydoc.getMap<number>('__tiptapcollab__config').set('intervalSeconds', 900)
```

## 恢复到某个版本

当您恢复到以前的版本时：

1. 如果有未保存的更改，Tiptap 会自动创建一个版本以保留这些更改。
2. Tiptap 会在历史记录的顶部创建一个新版本，其内容来自您选择的版本。
3. 所有用户可以从此新版本继续工作。

请注意，恢复仅影响 ydoc 中的 `default` 片段。当您恢复 Tiptap 内容时，评论不会更改（除非您在 TiptapCollabProvider 中指定了不同的 `field`）。

您可以集成 [比较快照](https://tiptap.zhcndoc.com/collaboration/documents/snapshot-compare.md) 扩展以突出不同版本之间的差异，确保您选择正确的版本进行恢复。

## 存储

| Key               | Type                   | Description                                                                                                                              |
| ----------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| currentVersion    | `number`               | 当前版本。                                                                                                                                    |
| lastSaved         | `Date`                 | 最近保存的时间戳。                                                                                                                                |
| latestVersion     | `number`               | 最新版本。                                                                                                                                    |
| provider          | `TiptapCollabProvider` | 协作提供者实例。                                                                                                                                 |
| status            | `string`               | 提供者的状态 - 可以是 `connecting`、`connected` 或 `disconnected`。                                                                                  |
| synced            | `boolean`              | 版本历史是否与服务器同步。                                                                                                                            |
| versioningEnabled | `boolean`              | 是否启用版本控制。                                                                                                                                |
| versions          | `array<Version>`       | 存储在历史中的版本数组。每个版本包含 `version`（数字）、`date`（数字）、可选的 `name`（字符串）和可选的 `meta`（对象）。有关 `meta` 字段的详细信息，请参见 [自动版本元数据](#automatic-version-metadata)。 |

## 命令

| 命令               | 描述                       |
| ---------------- | ------------------------ |
| saveVersion      | 使用给定的标题创建一个新版本           |
| toggleVersioning | 为此文档切换自动版本控制             |
| revertToVersion  | 恢复到特定版本，可以使用可选标题创建新的恢复版本 |

## 示例

### 基本设置

```js
const provider = new TiptapCollabProvider({
  // ...
})

const editor = new Editor({
  // ...
  extensions: [
    // ...
    Snapshot.configure({
      provider,
    }),
  ],
})
```

### 存储版本更新

在此示例中，我们检索版本更新的数据并将其保存到变量中。

```js
let currentVersion = 0
let latestVersion = 0
let autoversioningEnabled = false
let versions = []

const provider = new TiptapCollabProvider({
  // ...
})

const editor = new Editor({
  // ...
  extensions: [
    // ...
    Snapshot.configure({
      provider,
      onUpdate(payload) {
        currentVersion = payload.currentVersion
        latestVersion = payload.version
        versions = payload.versions
        autoversioningEnabled = payload.versioningEnabled
      },
    }),
  ],
})
```

### 直接从存储访问版本数据

```js
const provider = new TiptapCollabProvider({
  // ...
})

const editor = new Editor({
  // ...
  extensions: [
    // ...
    Snapshot.configure({
      provider,
    }),
  ],
})

const latestVersion = editor.storage.snapshot.latestVersion
const currentVersion = editor.storage.snapshot.currentVersion
const versions = editor.storage.snapshot.versions
const autoversioningEnabled = editor.storage.snapshot.versioningEnabled
```

### 手动创建新版本

```js
editor.commands.saveVersion('我的新自定义版本')
```

### 使用元数据创建版本

您可以向版本附加任意元数据。这对于存储额外的上下文信息（如作者、标签或任何自定义数据）非常有用。

```js
editor.commands.saveVersion('Release v1.0', false, {
  author: 'Jane Doe',
  tags: ['release', 'stable'],
})
```

`saveVersion` 命令接受三个参数：

| 参数    | 类型                        | 默认值         | 描述            |
| ----- | ------------------------- | ----------- | ------------- |
| name  | `string`                  | `undefined` | 版本的可选名称       |
| force | `boolean`                 | `false`     | 即使未检测到更改也创建版本 |
| meta  | `Record<string, unknown>` | `undefined` | 附加到版本的任意元数据   |

### 自动版本元数据

每个版本的 `meta` 对象中自动包含一个 `__tiptap` 键。该元数据由服务器添加，提供有关版本创建方式及贡献者的上下文信息。

一个版本的 `meta` 字段示例如下：

```json
{
  "__tiptap": {
    "trigger": "websocket",
    "changesBy": ["#user1"],
    "triggeredBy": "user1"
  },
  "wordCount": 81624434
}
```

`__tiptap` 对象包含以下字段：

| 字段          | 类型         | 描述                                                                                                                       |
| ----------- | ---------- | ------------------------------------------------------------------------------------------------------------------------ |
| trigger     | `string`   | 版本是如何创建的。可能的值：`"websocket"`（通过编辑器/提供程序）、`"api"`（通过 REST API）、`"autoVersioning"`（由自动版本控制功能自动创建）或 `"revert"`（在恢复到先前版本时创建）。 |
| changesBy   | `string[]` | 做出本版本所包含更改的用户标识符列表。这些标识符是贡献客户端在其 `TiptapCollabProvider` 上设置的 `user` 值。                                                   |
| triggeredBy | `string`   | 触发版本创建的用户标识符。对于 `"websocket"` 版本，这是已认证 JWT 的 `sub`；对于 `"api"` 版本，这是您的后端提供的 `user` 查询参数。                                  |

您通过 `saveVersion` 或 REST API 传递的任何自定义元数据都存储在 `__tiptap` 键的同一级 `meta` 顶层中。`__tiptap` 键为保留字段，不能被自定义元数据覆盖。

> **保护编辑归因:**
>
> 默认情况下，`changesBy` 反映的是每个客户端在其 `TiptapCollabProvider` 上设置的 `user` 值。这个值由客户端提供，**不会**与已认证的 JWT 进行校验，因此客户端可以冒用其他用户的身份记录更改。
>
> 要使归因可信，请启用 [`user_mapping_authenticator`](https://tiptap.zhcndoc.com/collaboration/operations/configure.md) 设置。启用后，服务器会拒绝任何注册、覆盖或删除与连接 JWT `sub` 不匹配的用户身份的尝试。启用此设置后，**每个客户端都必须将 `TiptapCollabProvider.user` 设置为其 JWT `sub`**，否则其连接将被拒绝。这需要启用 JWT 身份验证。

### 从服务器获取版本

您可以使用 `fetchVersions` 命令手动刷新服务器上的版本列表。这会更新扩展存储中的最新版本数据。

```js
editor.commands.fetchVersions()
```

### 启用文档自动版本控制

```js
editor.commands.enableVersioning()
```

### 禁用文档自动版本控制

```js
editor.commands.disableVersioning()
```

### 通过版本 ID 恢复

```js
editor.commands.revertToVersion(4)
```

### 使用自定义名称恢复版本 ID

在此示例中，编辑器命令帮助您回到版本 4。当您使用此命令时，它将带您回到版本 4 的状态，并且还将此旧版本保存为一个新版本，称为“恢复到版本”。这样，您可以继续从版本 4 开始工作，但它现在作为最新版本保存。

```js
editor.commands.revertToVersion(4, '恢复到版本')
```

### 恢复、命名和备份

在此示例中，当您恢复到文档的版本 4 时，编辑器会自动创建两个新版本。第一个新版本记录并保存您在恢复之前文档的状态，作为备份。第二个新版本将文档恢复到版本 4，允许您从这里继续作为新的起点。

```js
editor.commands.revertToVersion(4, '恢复到版本', '恢复之前的未版本化更改')
```

### 为编辑器实现版本预览

上述示例直接修改文档，并未提供版本的仅限于本地的预览。因此，您必须为此需求创建自己的前端解决方案。您可以利用 `TiptapCloudProvider` 的无状态消息系统请求特定版本的内容。

首先，附加一个监听器到提供程序：

```js
const versions = await provider.listVersions()
// versions: Array<{ version: number, date: number, name?: string, meta?: Record<string, unknown> }>
```

#### 获取缓存的版本列表

获取本地缓存的版本列表（不请求服务器）：

```js
const versions = provider.getVersions()
```

#### 监听版本变更

使用回调函数订阅版本列表更新：

```js
const onVersionsChange = (versions) => {
  console.log('版本已更新:', versions)
}

// 开始监听
provider.watchVersions(onVersionsChange)

// 停止监听
provider.unwatchVersions(onVersionsChange)
```

#### 创建带元数据的版本

```js
provider.createVersion('我的版本', false, {
  author: 'Jane Doe',
  tags: ['draft'],
})
```

### 为编辑器实现版本预览

上述示例直接修改文档，并不提供仅在本地对版本的预览。因此，您必须为此需求创建自己的前端解决方案。您可以使用 `TiptapCollabProvider` 上的 `previewVersion` 方法从服务器请求特定版本。

首先，附加一个监听器到提供程序：

```js
// 导入 getPreviewContentFromVersionPayload 辅助函数（详情见下文）
import { watchPreviewContent } from '@tiptap-pro/extension-snapshot'

// 配置提供程序
const provider = new TiptapCollabProvider({ ... })

// 使用 watchPreviewContent 工具函数监听提供者上的内容更改
const unbindWatchContent = watchPreviewContent(provider, content => {
  // 设置编辑器内容
  editor.commands.setContent(content)
})
```

如果您想解绑监听器，可以调用返回的 `unbindWatchContent` 函数：

```js
const unbindWatchContent = watchPreviewContent(provider, (content) => {
  // 设置编辑器内容
  editor.commands.setContent(content)
})

// 取消监听
unbindWatchContent()
```

配置完成后，您可以按如下方式触发 `version.preview` 请求：

```js
// 向服务器请求特定版本的预览
provider.previewVersion(1)

// 您可以将此操作绑定到按钮点击或其他 UI 元素上以触发请求。
```

要超越预览并以可视化方式比较不同版本，[比较快照](https://tiptap.zhcndoc.com/collaboration/documents/snapshot-compare.md) 扩展提供了一种简单的方法来查看编辑器中任意两个版本之间的差异。

## 实用函数

### getPreviewContentFromVersionPayload

此函数将来自协作提供程序的有效负载转换为 Tiptap JSON 内容。

| 参数      | 描述                      |
| ------- | ----------------------- |
| payload | 版本预览事件的 Hocuspocus 有效负载 |
| field   | 要解析的字段。默认值：`default`    |

```js
const myContent = getPreviewContentFromVersionPayload(payload, 'default')
```

### watchPreviewContent

此函数在您的提供程序上设置一个观察者，监控必要的事件以响应版本内容的更改。它还返回一个新函数，您可以用来取消观察这些事件。

| 参数       | 描述                          |
| -------- | --------------------------- |
| provider | 协作提供程序                      |
| callback | 被调用的回调函数，参数为 Tiptap JSON 内容 |
| field    | 被监视的字段 - 默认值为 `default`     |

```js
const unwatchContent = watchPreviewContent(provider, editor.commands.setContent, 'default')

// 取消观察版本预览内容
unwatchContent()
```

## 可能的提供程序有效负载

以下是可以从提供程序发送或接收的有效负载列表：

### 外发

#### `document.revert`

请求将文档恢复到给定版本，带有可选的标题设置。

```js
provider.createVersion('我的自定义版本', false, { author: 'Jane Doe' })
```

#### `version.create`

创建一个带有可选标题的新版本。

```js
provider.sendStateless(
  JSON.stringify({ action: 'version.create', name: '我的自定义版本' }),
)
```

#### `versions.list`

请求服务器上的全部版本列表。

```js
const versions = await provider.listVersions()
```

### 内发

#### `version.created`

此无状态消息包含有关新创建版本的信息，包括任何附加的元数据。

```js
provider.on('stateless', (data) => {
  const payload = JSON.parse(data.payload)

  if (payload.event === 'version.created') {
    const latestVersion = payload.version
    const name = payload.name // 可选版本名称
    const date = payload.date // 创建时间戳
    const meta = payload.meta // 可选元数据对象
  }
})
```

#### `versions.list`

`listVersions` 方法返回完整的版本列表。每个版本包含 `version`、`date`，以及可选的 `name` 和 `meta` 字段。

```js
const versions = await provider.listVersions()
// versions 是一个包含 { version: number, date: number, name?: string, meta?: object } 的数组
```

#### `document.reverted`

此无状态消息包含有关文档恢复的信息。

```js
provider.on('stateless', (data) => {
  const payload = JSON.parse(data.payload)

  if (payload.event === 'document.reverted') {
    const currentVersion = payload.version
  }
})
```
