gutenbergdocs/docs/reference-guides/block-api/block-transforms.md
2025-10-22 01:40:18 +08:00

355 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## `ungroup` 区块解组功能
通过区块配置中可选的 `transforms` 键,区块可使用 `ungroup` 子键定义用于替换当前处理区块的新区块。这些新区块通常是现有内部区块的子集,但也可包含全新区块。
若某区块配置了 `ungroup` 转换功能,即具备解组资格,无需作为默认分组区块即可使用。通过此 API 解组区块的用户界面与默认分组区块所用的界面相同。需满足以下条件才会显示解组按钮:必须选中单个分组区块,且该区块需包含若干内部区块。
**ungroup** 是一个回调函数,接收被处理区块的属性及内部区块数据,需返回区块对象数组。
示例:
```js
export const settings = {
title: '我的分组区块标题',
description: '我的分组区块描述',
/* ... */
transforms: {
ungroup: ( attributes, innerBlocks ) =>
innerBlocks.flatMap( ( innerBlock ) => innerBlock.innerBlocks ),
},
};
```
当我们成功匹配此内容时,除 `data-post-id` 外所有HTML属性都将被剥离。如果给定 `div` 内存在其他HTML排列方式则无法匹配转换器。同理若其中出现的是 `<h3>` 而非 `<h2>`,匹配也会失败。
在处理包含非短语内容(如带 `<summary>``<details>`的HTML片段时模式定义至关重要。若未声明自定义模式编辑器在尝试通过任何区块转换前会跳过这些特殊结构。
### 短代码
此类转换支持单向转换(从短代码创建区块),作为 `raw` 转换流程的组成部分。
`shortcode` 类型转换对象包含以下参数:
- **type** _(字符串)_:固定值 `shortcode`
- **tag** _(字符串|数组)_:可转换的短代码标签或别名列表
- **transform** _(函数,可选)_:接收短代码属性(首参数)与 [WPShortcodeMatch](/packages/shortcode/README.md#next)(次参数)的回调函数,应返回区块对象或区块对象数组。定义此参数时将优先于 `attributes` 参数
- **attributes** _(对象,可选)_:根据[区块配置对象](/docs/reference-guides/block-api/block-registration.md)定义的属性结构,指定区块属性来源。若某属性包含 `shortcode` 键,则应为接收短代码属性(首参数)与 [WPShortcodeMatch](/packages/shortcode/README.md#next)(次参数)的函数,并返回将存入区块注释的属性值
- **isMatch** _(函数,可选)_:根据 [Shortcode API](https://codex.wordpress.org/Shortcode_API) 接收短代码属性的回调函数,应返回布尔值。返回 `false` 将阻止该短代码转换为此区块
- **priority** _(数字,可选)_:控制转换应用优先级,数值较低者优先,工作机制类似 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。如未设置,默认优先级为 `10`
**示例:通过 `transform` 从短代码转换为区块**
使用 `transform` 方法可将现有短代码转换为其对应区块:
```js
transforms: {
from: [
{
type: 'shortcode',
tag: 'video',
transform( { named: { src } } ) {
return createBlock( 'core/video', { src } );
},
// 当短代码不具备正确ID时
// 阻止其转换为此区块
isMatch( { named: { id } } ) {
return id === 'my-id';
},
},
],
},
```
**示例:通过 `attributes` 从短代码转换为区块**
使用 `attributes` 参数可将现有短代码转换为其对应区块:
```js
transforms: {
from: [
{
type: 'shortcode',
tag: 'youtube',
attributes: {
url: {
type: 'string',
source: 'attribute',
attribute: 'src',
selector: 'img',
},
align: {
type: 'string',
// 短代码函数将提取短代码属性
// 转换为可存入区块注释的值
shortcode: ( { named: { align = 'alignnone' } } ) => {
return align.replace( 'align', '' );
},
},
},
// 当短代码不具备正确ID时
// 阻止其转换为此区块
isMatch( { named: { id } } ) {
return id === 'my-id';
},
},
]
},
```
# 转换功能
区块转换是一套API允许区块与其他区块进行相互转换同时也支持从其他实体转换为区块。与此API兼容的现有实体包括短代码、文件、正则表达式和原始DOM节点。
## 转换方向:`to` 与 `from`
区块通过配置项中可选的 `transforms` 键来声明支持的转换类型,其子键 `to``from` 分别存储不同方向的转换数组。例如:
```js
export const settings = {
title: '我的区块标题',
description: '我的区块描述',
/* ... */
transforms: {
from: [
/* 支持的来源转换 */
],
to: [
/* 支持的目标转换 */
],
},
};
```
## 转换类型
本节将介绍区块支持的现有转换类型:
- block区块
- enter回车
- files文件
- prefix前缀
- raw原始
- shortcode短代码
### 区块转换
此类转换支持双向转换允许区块转换为其他类型的区块。在区块工具栏中有对应的UI控件。
`block` 类型的转换对象包含以下参数:
- **type**(字符串):值为 `block`
- **blocks**(数组):已知区块类型列表。支持通配符值(`"*"`),表示该转换适用于所有区块类型(例如:所有区块都能转换为 `core/group`
- **transform**(函数):接收被处理区块属性及内部区块的回调函数,应返回区块对象或区块对象数组
- **isMatch**(函数,可选):接收区块属性(第一参数)和区块对象(第二参数)的回调函数,应返回布尔值。返回 `false` 将隐藏该转换选项
- **isMultiBlock**布尔值可选是否支持多选区块转换。为true时`transform` 函数的首个参数为属性数组第二参数为内部区块数组。默认为false
- **priority**(数字,可选):控制转换优先级,数值越低优先级越高。行为类似[WordPress钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress),默认优先级为 `10`
**示例:从段落区块转换为标题区块**
在标题区块配置中添加以下代码(使用 [`wp-blocks` 包](/packages/blocks/README.md#createBlock) 的 `createBlock` 函数):
```js
transforms: {
from: [
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) => {
return createBlock( 'core/heading', {
content,
} );
},
},
]
},
```
**示例:含内部区块的转换**
具有InnerBlocks的区块可与其他含内部区块的区块相互转换
```js
transforms: {
to: [
{
type: 'block',
blocks: [ 'some/block-with-innerblocks' ],
transform: ( attributes, innerBlocks ) => {
return createBlock(
'some/other-block-with-innerblocks',
attributes,
innerBlocks
);
},
},
],
},
```
### 回车转换
此类转换仅支持来源转换,允许根据用户输入内容创建区块。当用户输入内容后按回车键时,将在新行中触发转换。
`enter` 类型的转换对象包含以下参数:
- **type**(字符串):值为 `enter`
- **regExp**(正则表达式):用于匹配的正则表达式,匹配成功则触发转换
- **transform**(函数):接收包含输入内容的回调函数,应返回区块对象或区块对象数组
- **priority**(数字,可选):控制转换优先级,数值越低优先级越高。行为类似[WordPress钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress),默认优先级为 `10`
**示例:通过 --- 创建分隔符区块**
当用户输入三个连字符后按回车键时创建分隔符区块:
```js
transforms = {
from: [
{
type: 'enter',
regExp: /^-{3,}$/,
transform: () => createBlock( 'core/separator' ),
},
],
};
```
### 文件类型转换
此类转换支持 _from_ 方向,允许通过将文件拖拽至编辑器来创建区块。
`files` 类型的转换是一个包含以下参数的对象:
- **type**(字符串):取值为 `files`
- **transform**(函数):接收正在处理的文件数组的回调函数,应返回区块对象或区块对象数组
- **isMatch**(函数,可选):接收正在处理的文件数组的回调函数,应返回布尔值。返回 `false` 将阻止应用此转换
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
**示例:从文件创建文件区块**
当用户将文件拖拽至编辑器时,可通过以下代码创建文件区块:
```js
transforms: {
from: [
{
type: 'files',
isMatch: ( files ) => files.length === 1,
// 通过定义低于默认值10的优先级
// 使其在没有找到其他转换时作为备用方案创建文件区块
priority: 15,
transform: ( files ) => {
const file = files[ 0 ];
const blobURL = createBlobURL( file );
// 文件将在 componentDidMount() 中上传
return createBlock( 'core/file', {
href: blobURL,
fileName: file.name,
textLinkHref: blobURL,
} );
},
},
];
}
```
### 前缀类型转换
此类转换支持 _from_ 方向,允许根据用户输入的文本创建区块。当用户在新区块行中输入文本并添加尾随空格时触发。
`prefix` 类型的转换是一个包含以下参数的对象:
- **type**(字符串):取值为 `prefix`
- **prefix**(字符串):匹配此转换的字符或字符序列
- **transform**(函数):接收输入内容的回调函数,应返回区块对象或区块对象数组
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
**示例:从文本创建自定义区块**
若要在用户输入问号时创建自定义区块,可使用以下代码:
```js
transforms: {
from: [
{
type: 'prefix',
prefix: '?',
transform( content ) {
return createBlock( 'my-plugin/question', {
content,
} );
},
},
];
}
```
### 原始内容类型转换
此类转换支持 _from_ 方向,允许根据原始 HTML 节点创建区块。当用户执行区块设置界面菜单中的"转换为区块"操作,或向编辑器粘贴/拖拽内容时触发。
`raw` 类型的转换是一个包含以下参数的对象:
- **type**(字符串):取值为 `raw`
- **transform**(函数,可选):接收正在处理节点的回调函数,应返回区块对象或区块对象数组
- **schema**(对象|函数,可选):定义用于检测和处理粘贴内容的 [HTML 内容模型](https://html.spec.whatwg.org/multipage/dom.html#content-models),详见[下文](#内容模型与架构)
- **selector**(字符串,可选):用于根据 [element.matches](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) 方法判断元素是否匹配的 CSS 选择器字符串。若元素不匹配则不会执行转换。这是 `isMatch` 的简写替代方案,若同时存在则优先使用 `isMatch`
- **isMatch**(函数,可选):接收正在处理节点的回调函数,应返回布尔值。返回 `false` 将阻止应用此转换
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
**示例:从 URL 创建嵌入区块**
若要在用户向编辑器粘贴 URL 时创建嵌入区块,可使用以下代码:
```js
transforms: {
from: [
{
type: 'raw',
isMatch: ( node ) =>
node.nodeName === 'P' &&
/^\s*(https?:\/\/\S+)\s*$/i.test( node.textContent ),
transform: ( node ) => {
return createBlock( 'core/embed', {
url: node.textContent.trim(),
} );
},
},
],
}
```
<h4 id="内容模型与架构">内容模型与架构</h4>
在粘贴内容时,可以定义用于验证和处理粘贴内容的[内容模型](https://html.spec.whatwg.org/multipage/dom.html#content-models)。粘贴到编辑器中的 HTML 通常包含应当转移和不应转移的混合元素。例如考虑将 `<span class="time">12:04 pm</span>` 粘贴到编辑器中的情况:我们需要复制 `12:04 pm` 并忽略 `<span>` 及其 `class` 属性,因为这些元素在复制后不再具有原有的含义和结构。
在编写 `raw` 转换时,可通过提供描述允许内容的 `schema` 来控制此行为,该架构将在尝试与区块匹配之前对粘贴内容进行清理。这些架构会传入 [`@wordpress/dom` 中的 `cleanNodeList`](https://github.com/wordpress/gutenberg/blob/trunk/packages/dom/src/dom/clean-node-list.js);请查阅该处获取[架构的完整描述](https://github.com/wordpress/gutenberg/blob/trunk/packages/dom/src/phrasing-content.js)。
```js
schema = { span: { children: { '#text': {} } } };
```
**示例:自定义内容模型**
假设我们需要匹配以下 HTML 片段并将其转换为某种自定义文章预览区块:
```html
<div data-post-id="13">
<h2>文章标题</h2>
<p>一段<em>精彩</em>内容</p>
</div>
```
我们需要通过提供以下架构来告知编辑器允许内部的 `h2``p` 元素。此示例中使用函数形式,该函数接收包含 `phrasingContentSchema` 的参数(以及指示转换操作是否以粘贴文本开始的布尔值 `isPaste`)。`phrasingContentSchema` 预定义为匹配 HTML 短语元素,如 `<strong>`、`<sup>` 和 `<kbd>`。任何期望使用 `<RichText />` 组件的地方都适合允许短语内容,否则在转换过程中将丢失所有文本格式。
```js
schema = ({ phrasingContentSchema }) => {
div: {
required: true,
attributes: [ 'data-post-id' ],
children: {
h2: { children: phrasingContentSchema },
p: { children: phrasingContentSchema }
}
}
}
```