## 修改属性集

有时需要更新属性集，以重命名或修改旧属性。

**示例**

```js
const { registerBlockType } = wp.blocks;

registerBlockType( 'gutenberg/block-with-deprecated-version', {
	// ... 其他区块属性

	attributes: {
		content: {
			type: 'string',
			default: '随机值',
		},
	},

	save( props ) {
		return <div>{ props.attributes.content }</div>;
	},

	deprecated: [
		{
			attributes: {
				text: {
					type: 'string',
					default: '随机值',
				},
			},

			migrate( { text } ) {
				return {
					content: text,
				};
			},

			save( props ) {
				return <p>{ props.attributes.text }</p>;
			},
		},
	],
} );
```

在上方示例中，我们将区块的标记从 `p` 更新为 `div`，并将 `text` 属性重命名为 `content`。

## 修改内部区块

某些情况下，在迁移区块时可能需要添加或移除内部区块。
例如：某个区块需要将标题属性迁移至段落内部区块。

**示例**

```js
const { registerBlockType } = wp.blocks;

registerBlockType( 'gutenberg/block-with-deprecated-version', {
	// ... 区块属性

	save( props ) {
		return <p>{ props.attributes.title }</p>;
	},

	deprecated: [
		{
			attributes: {
				title: {
					type: 'string',
					source: 'html',
					selector: 'p',
				},
			},

			migrate( attributes, innerBlocks ) {
				const { title, ...restAttributes } = attributes;

				return [
					restAttributes,
					[
						createBlock( 'core/paragraph', {
							content: attributes.title,
							fontSize: 'large',
						} ),
						...innerBlocks,
					],
				];
			},

			save( props ) {
				return <p>{ props.attributes.title }</p>;
			},
		},
	],
} );
```

在上方示例中，我们将区块更新为使用带标题的内部段落区块，而非标题属性。

_以上是区块弃用的示例案例。如需查看真实场景的示例，请参阅[核心区块库](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src)中的弃用实现。核心区块在版本迭代中持续更新，包含简单与复杂的弃用方案。_

# 弃用说明

> 本页面提供了关于弃用API原理与使用的完整指南。入门教程请参阅[开发者博客](https://developer.wordpress.org/news/)上的[区块弃用基础教程](https://developer.wordpress.org/news/2023/03/block-deprecation-a-tutorial/)。

当更新静态区块标记与属性时，区块开发者需要考虑使用旧版区块的现有文章。为提供平滑的升级路径，您可选择以下策略之一：

-   不弃用原区块，创建新区块（使用不同名称）
-   提供区块的"弃用"版本，允许用户在区块编辑器中打开旧版内容时使用更新后的区块进行编辑。

一个区块可包含多个弃用版本。当解析后的区块当前状态无效，或弃用版本定义了返回true的`isEligible`函数时，将尝试执行弃用流程。

弃用机制并非像数据库迁移等软件数据更新那样以链式更新方式运作。初看容易认为每个弃用版本会对数据执行必要修改，然后将新形态的区块传递给下一个弃用版本继续处理。但实际流程是：

1.  若当前`save`方法无法生成有效区块，弃用数组中的第一个弃用版本将接收原始保存内容
2. 若该版本的`save`方法能生成有效内容，则使用此弃用版本来解析区块属性。若其包含`migrate`方法，系统将使用弃用版本解析的属性运行该方法
3. 若首个弃用版本的`save`方法无法生成有效区块，则依次尝试数组中后续弃用版本，直到找到能生成有效区块的版本
4. 首个生成有效区块的弃用版本对应的属性及innerBlocks，将被传回当前`save`方法以生成新的有效区块内容
5. 此时当前区块应处于有效状态，弃用工作流程随即终止

需特别注意：若弃用版本的`save`方法无法生成有效区块，则其`migrate`方法也会被完全跳过——即使`isEligible`函数对给定属性返回true。这意味着若区块存在多个弃用版本且需执行新迁移（如将内容移至`InnerBlocks`），可能需要更新多个弃用版本中的`migrate`方法，以确保所有历史版本都能应用必要更改。

另需注意：若弃用版本的`save`方法从其他文件导入额外函数，对这些文件的修改可能意外改变弃用行为。建议将这些函数的快照副本添加到弃用文件中，而非直接导入，以避免意外破坏弃用功能。

对于含多个弃用版本的区块，建议将每个弃用版本保存为对应区块版本的常量，再将其添加到区块的`deprecated`数组。数组中的弃用版本应按时间倒序排列，这样区块编辑器会优先尝试应用最新且最可能的弃用版本，避免不必要的性能损耗。

**示例**

```js
const v1 = {};
const v2 = {};
const v3 = {};
const deprecated = [ v3, v2, v1 ];
```

同时建议维护包含不同版本区块内容的[测试固件](https://github.com/WordPress/gutenberg/blob/HEAD/test/integration/fixtures/blocks/README.md)，以便轻松验证新弃用版本和迁移功能在所有历史版本中的运行情况。

弃用定义位于区块类型的`deprecated`属性中，这是一个弃用对象数组，每个对象包含以下形式：

-   `attributes` (对象)：区块弃用版本的[属性定义](/docs/reference-guides/block-api/block-attributes.md)
-   `supports` (对象)：区块弃用版本的[支持定义](/docs/reference-guides/block-api/block-registration.md)
-   `save` (函数)：区块弃用版本的[save实现](/docs/reference-guides/block-api/block-edit-save.md)
-   `migrate`：(函数，可选)。接收旧属性与内部区块后，应返回新属性或与区块兼容的属性元组数组。如前所述，若弃用版本的`save`函数未返回有效区块，其`migrate`将不会运行，因此需确保迁移功能在所有相关弃用版本中均可用。
    - _参数_
        - `attributes`: 区块旧属性
        - `innerBlocks`: 区块旧内部区块
    - _返回值_
        - `Object | Array`: 更新后的区块属性或元组数组`[attributes, innerBlocks]`
-   `isEligible`：(函数，可选)。当函数返回`true`时，即使区块有效仍可由该弃用版本处理迁移。这在区块技术上有效但仍需更新属性或内部区块时特别有用。**注意**：当所有先前弃用版本的save函数结果均无效时，此函数不会被调用。
    - _参数_
        - `attributes`: 从序列化HTML解析得到的原始区块属性（在应用区块类型代码之前）
        - `innerBlocks`: 区块当前内部区块
        - `data`: 包含代表区块节点及其结果区块对象属性的对象
            - `data.blockNode`: 解析序列化HTML后得到的区块原始形态
            - `data.block`: 对`blockNode`应用区块类型后得到的区块对象
    - _返回值_
        - `boolean`: 指示此有效区块是否符合该弃用版本的迁移条件

<div class="callout callout-alert">
重要提示：由于<code>attributes</code>、<code>supports</code>和<code>save</code>会影响区块的解析与序列化，它们不会自动从当前版本继承，必须在弃用对象中明确定义才能在迁移过程中被处理。
</div>

**示例**

```js
const { registerBlockType } = wp.blocks;
const attributes = {
	text: {
		type: 'string',
		default: 'some random value',
	},
};
const supports = {
	className: false,
};

registerBlockType( 'gutenberg/block-with-deprecated-version', {
	// ... 其他区块属性

	attributes,

	supports,

	save( props ) {
		return <div>{ props.attributes.text }</div>;
	},

	deprecated: [
		{
			attributes,

			supports,

			save( props ) {
				return <p>{ props.attributes.text }</p>;
			},
		},
	],
} );
```

以上示例中，我们将区块标记从`p`标签更新为`div`标签。