# 为什么需要文档引擎

> Fabric.js 负责绘制和序列化。Fabric.js 文档引擎补上真实编辑器需要的部分：稳定 id、安全保存、撤销重做和恢复。

Source: https://fabricjs-document-engine.jscrate.dev/zh/docs/overview/why-document-engine
Last updated: 2026-09-28

Fabric.js 负责绘制对象、处理鼠标交互，并把画布转成 JSON。可一个画布编辑器需要的不止这些。Fabric.js 文档引擎把这些能力组织成一份文档，让你可以保存、重新打开、撤销和恢复。

## `toJSON` 和 `loadFromJSON` 没帮你做的事

大多数编辑器一开始都用 `canvas.toJSON()` 和 `canvas.loadFromJSON()`。做演示没问题，放进真实产品后，下面这些问题会一个个冒出来：

- **自定义属性丢失。** `toJSON` 会丢掉它不认识的属性，除非你每次调用都把它们列出来。
- **对象没有稳定的 id。** 你没法把一个图形和评论或数据库里的一行关联起来，因为保存前后没有东西能标识它。
- **加载失败后画布只剩一半。** 只要有一张图片丢了，`loadFromJSON` 可能已经清空画布，只加载了部分内容。
- **两次加载互相抢。** 用户先打开文档 A，又马上打开 B。如果 A 最后才加载完，显示的就是 A。
- **旧的保存覆盖新的。** 一次很慢的保存可能晚于更新的保存完成。另一个标签页也可能不声不响地覆盖你的内容。
- **没有撤销。** Fabric.js 本身没有撤销重做。每次改动都给整个画布拍快照，图一大就会变慢。

## 引擎补上了什么

| 问题             | 引擎的做法                                           |
| ---------------- | ---------------------------------------------------- |
| 没有 id          | 每个对象，包括编组里的子对象，都有稳定的对象 id      |
| 自定义属性丢失   | 类和它的属性注册一次就够了                           |
| 画布只加载了一半 | 动画布之前，先校验文档并检查每张图片                 |
| 加载互相抢       | 最新的加载生效，较早的被取消                         |
| 旧保存覆盖新保存 | 同一时间只有一次保存，并用修订号发现其他标签页的改动 |
| 没有撤销         | 每个用户操作一步撤销，只存变化的部分                 |
| 崩溃后内容丢失   | 用户编辑时，未保存的内容会复制到 IndexedDB           |

画布、工具栏和界面都还是你自己的。Fabric.js 文档引擎只监听画布，只在你加载、撤销或恢复时才改动它。

## 它不做的事

- 不画工具栏，也不提供任何界面。按钮由你的应用渲染。
- 不托管你的数据。你接入自己的存储，或者用内置的 localStorage 适配器。
- 不替代 Fabric.js。对象还是用 Fabric.js 的类来创建。
- 不做实时协作。修订号能发现冲突，但两个人不能同时在线编辑。

## 什么时候用它

当用户会在你的画布编辑器里保存作品，并希望之后还能找到它时，就适合用 Fabric.js 文档引擎：设计工具、户型图、标签设计器、白板、图片标注等。只画一次、从不保存的场景，用 Fabric.js 本身就够了。

## 下一步

- [快速开始](https://fabricjs-document-engine.jscrate.dev/zh/docs/overview/quick-start)：最小的可运行配置。
- [对比](https://fabricjs-document-engine.jscrate.dev/zh/docs/overview/comparison)：和撤销插件、手写保存逻辑相比有什么不同。
