# 实体与撤销/重做

无论是文章编辑器还是站点编辑器，WordPress 编辑器操作的都是我们称为实体记录的对象。这些对象代表着文章、页面、用户、分类项、模板等数据。它们既是存储在数据库中的数据，也是被编辑器操作的对象。每个编辑器都能同时获取、编辑和保存多个实体记录。

例如在站点编辑器中打开页面时：

- 您可以编辑页面本身的属性（标题、内容等）
- 您可以编辑页面模板的属性（模板内容、设计等）
- 您可以编辑模板所使用的模板部件属性（页眉、页脚等）

编辑器会追踪所有这些修改，并协调所有已修改记录的保存操作。这些功能都在 `@wordpress/core-data` 包中实现。

## 编辑实体

要编辑实体，首先需要获取并将其加载到 `core-data` 存储中。例如以下代码将 ID 为 1 的文章加载到存储中（实体是文章，文章 1 是实体记录）。

````js
wp.data.select( 'core' ).getEntityRecord( 'postType', 'post', 1 );
````

实体加载后即可进行编辑。例如以下代码将文章标题设置为 "Hello World"。对于每个获取的实体记录，`core-data` 存储会追踪以下内容：

- **“持久化”记录**：从后端获取时记录的最终状态
- **“编辑”列表**：记录一个或多个属性的未保存本地修改

该程序包还提供了一系列操作来管理已获取的实体记录。

要编辑实体记录，可以调用 `editEntityRecord` 方法，该方法需要传入实体类型、实体 ID 和新实体记录作为参数。以下示例将 ID 为 1 的文章标题设置为 "Hello World"。

````js
wp.data.dispatch( 'core' ).editEntityRecord( 'postType', 'post', 1, { title: 'Hello World' } );
````

编辑实体记录后即可进行保存。以下代码保存了 ID 为 1 的文章。

````js
wp.data.dispatch( 'core' ).saveEditedEntityRecord( 'postType', 'post', 1 );
````

## 撤销/重做

由于 WordPress 编辑器允许同时编辑多个实体记录，`core-data` 包会在公共的撤销/重做堆栈中追踪所有已获取和编辑的实体记录。撤销/重做堆栈中的每个步骤都包含一个“编辑”列表，在调用 `undo` 或 `redo` 操作时需要同时执行这些编辑。

为了正确执行撤销和重做操作，每个编辑列表中的修改都包含以下信息：

- **实体种类与名称**：core-data 中的每个实体都由_(种类, 名称)_对标识，对应被修改实体的标识符
- **实体记录 ID**：被修改记录的 ID
- **属性**：被修改属性的名称
- **原值**：属性的原始值（用于执行撤销操作）
- **新值**：属性的新值（用于执行重做操作）

例如，假设用户先编辑了文章标题，接着修改了文章别名，然后又修改了文章中使用的可重用块标题。撤销/重做堆栈中将存储以下信息：

- `[ { kind: 'postType', name: 'post', id: 1, property: 'title', from: '', to: 'Hello World' } ]`
- `[ { kind: 'postType', name: 'post', id: 1, property: 'slug', from: 'Previous slug', to: 'This is the slug of the hello world post' } ]`
- `[ { kind: 'postType', name: 'wp_block', id: 2, property: 'title', from: 'Reusable Block', to: 'Awesome Reusable Block' } ]`

存储还会维护一个指向当前“撤销/重做”步骤的“指针”。默认情况下，指针始终指向堆栈中的最后一个项目。当用户执行撤销或重做操作时，该指针会相应更新。

### 缓存变更

撤销/重做的核心行为还支持所谓的“缓存修改”。这些修改不会立即存储在撤销/重做堆栈中。例如当用户在文本字段中输入时，字段值会在存储中修改，但该修改只有在用户移动到下一个单词或经过几毫秒后才会存入撤销/重做堆栈。这样做是为了避免为用户输入的每个字符都创建新的撤销/重做步骤。

缓存变更会被保留在撤销/重做堆栈之外的“修改缓存”中，只有当显式调用 `__unstableCreateUndoLevel` 或当下一个修改不是缓存修改时，这些修改才会存入撤销/重做堆栈。

默认情况下，所有对 `editEntityRecord` 的调用都被视为“非缓存”操作，除非传入 `isCached` 选项为 true。例如：

```js
wp.data.dispatch( 'core' ).editEntityRecord( 'postType', 'post', 1, { title: 'Hello World' }, { isCached: true } );
```