概述

在区块编辑器出现之前,自定义元数据框(meta boxes)被用于扩展编辑器。现在有了新的扩展方式,赋予了开发者更多的能力,并为作者提供了更好的体验。建议将旧的自定义元数据框迁移到这些新方法之一,以创建更统一和一致的编辑器使用体验。

区块编辑器支持大多数现有的元数据框,有关详细信息,请参见 下方的向后兼容性部分 。

如果您有兴趣在编辑器之外处理文章元数据(post meta),请查看 侧边栏教程。

使用区块存储元数据

通常,区块将属性值存储在序列化的区块 HTML 中。但是,您也可以创建一个区块,将其属性值保存为文章元数据(post meta),从而可以在模板的任何位置通过编程方式访问这些数据。

本指南展示了如何创建一个提示用户输入单个值的区块,并将其保存到文章元数据中。

开始之前

本指南假设您已经熟悉 WordPress 插件、文章元数据和基本的 JavaScript。请复习 JavaScript 入门教程 以获取介绍。

本指南将指导您创建一个基本区块,但建议通过 创建区块教程 来更深入地了解如何创建自定义区块。

您需要:

  • WordPress 开发环境,
  • 一个已激活并准备好编辑的最小化插件
  • 用于构建和加载的 JavaScript 设置

有一个 完整的元数据区块示例,您可以将其用作设置的参考。

分步指南

  1. 注册元数据字段
  2. 添加元数据区块
  3. 使用文章元数据
  4. 最后修饰(可选)

步骤 1:注册元数据字段

文章元数据字段是一个用于存储有关文章的额外数据的 WordPress 对象。您需要在使用之前首先注册一个新的元数据字段。请查看管理 文章元数据 以了解更多关于文章元数据的信息。

在注册字段时,请注意 show_in_rest 参数。这确保数据将包含在 REST API 中,区块编辑器使用 REST API 来加载和保存元数据。请查看 register_post_meta 函数定义以获取更多信息。

此外,您的文章类型需要支持 custom-fields 才能使 register_post_meta 函数正常工作。

要注册字段,请将以下内容添加到您的 PHP 插件中:

<?php
// register custom meta tag field
function myguten_register_post_meta() {
    register_post_meta( 'post', 'myguten_meta_block_field', array(
        'show_in_rest' => true,
        'single' => true,
        'type' => 'string',
    ) );
}
add_action( 'init', 'myguten_register_post_meta' );

步骤 2:添加元数据区块

在上一步注册了元数据字段后,接下来创建一个新区块来向用户显示该字段的值。

钩子 useEntityProp 可用于区块获取或更改元数据值。

将此代码添加到 JavaScript src/index.js:

import { registerBlockType } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { useEntityProp } from '@wordpress/core-data';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'myguten/meta-block', {
    edit: ( { setAttributes, attributes } ) => {
        const blockProps = useBlockProps();
        const postType = useSelect(
            ( select ) => select( 'core/editor' ).getCurrentPostType(),
            []
        );

        const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );

        const metaFieldValue = meta[ 'myguten_meta_block_field' ];
        const updateMetaValue = ( newValue ) => {
            setMeta( { ...meta, myguten_meta_block_field: newValue } );
        };

        return (
            <div { ...blockProps }>
                <TextControl
                    label="Meta Block Field"
                    value={ metaFieldValue }
                    onChange={ updateMetaValue }
                />
            </div>
        );
    },

    // No information saved to the block.
    // Data is saved to post meta via the hook.
    save: () => {
        return null;
    },
} );

通过创建一篇文章并添加元数据区块来确认此功能是否有效。您将看到可以输入值的字段。当您保存文章(无论是草稿还是已发布)时,文章元数据值也会被保存。您可以通过保存并重新加载您的草稿来验证这一点,表单在重新加载后仍然会被填充。

您也可以通过检查数据库表 wp_postmeta 来确认数据是否已保存,并确认新的文章 ID 包含新字段的数据。

故障排除:请确保在更改之间构建您的代码,您更新了步骤 1 中的 PHP 代码,并且 JavaScript 文件已加载。检查构建输出和开发者控制台以查找错误。

步骤 3:使用文章元数据

您可以以多种方式使用上一步中存储的文章元数据。

在 PHP 中使用文章元数据

第一个示例使用来自文章元数据字段的值,并将其附加到用 H4 标签包裹的文章内容的末尾。

function myguten_content_filter( $content ) {
    $value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
    if ( $value ) {
        return sprintf( "%s <h4> %s </h4>", $content, esc_html( $value ) );
    } else {
        return $content;
    }
}
add_filter( 'the_content', 'myguten_content_filter' );

在区块中使用文章元数据

您也可以在其它区块中使用文章元数据。对于此示例,当 Paragraph 区块被渲染(即显示给用户)时,会在每个 Paragraph 区块的末尾加载数据。您可以根据需要将其替换为任何核心或自定义区块类型。

在 PHP 中,使用 register_block_type 函数设置一个回调,当区块被渲染时包含元数据值。

function myguten_render_paragraph( $block_attributes, $content ) {
    $value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
    // check value is set before outputting
    if ( $value ) {
        return sprintf( "%s (%s)", $content, esc_html( $value ) );
    } else {
        return $content;
    }
}

register_block_type( 'core/paragraph', array(
    'api_version' => 3,
    'render_callback' => 'myguten_render_paragraph',
) );

步骤 4:使用区块模板(可选)

使用元数据区块的一个问题是,作者很容易忘记该区块,因为它需要添加到每篇文章中。您可以通过使用 区块模板 来解决这个问题。区块模板是每个文章类型的预定义区块项目列表。模板允许您为文章类型指定默认的初始状态。

对于此示例,您使用模板自动在文章顶部插入元数据区块。

将以下代码添加到 myguten-meta-block.php 文件:

function myguten_register_template() {
    $post_type_object = get_post_type_object( 'post' );
    $post_type_object->template = array(
        array( 'myguten/meta-block' ),
    );
}
add_action( 'init', 'myguten_register_template' );

您还可以在数组中添加其他区块类型,包括占位符,甚至可以将文章锁定到一组特定的区块。模板是控制编辑体验的强大工具,请查看上述文档以获取更多信息。

结论

本指南展示了如何使用区块读取和写入文章元数据。请参见下方的向后兼容性部分。

向后兼容性

测试、转换和维护现有元数据框

在将元数据框转换为区块之前,可能更容易测试元数据框是否与区块编辑器兼容,并明确标记为兼容。

如果元数据框 不 与区块编辑器兼容,并且更新它以使其正常工作不是选项,下一步是在元数据框声明中添加 __block_editor_compatible_meta_box 参数:

add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
    null, 'normal', 'high',
    array(
        '__block_editor_compatible_meta_box' => false,
    )
);

WordPress 将不会显示该元数据框,但会显示一条消息,说明它不与区块编辑器兼容,并包含指向经典编辑器插件的链接。默认情况下,__block_editor_compatible_meta_box 为 true。

在将元数据框转换为区块后,可以声明其存在以支持向后兼容性:

add_meta_box( 'my-meta-box', '我的元数据框', 'my_meta_box_callback',
    null, 'normal', 'high',
    array(
        '__back_compat_meta_box' => true,
    )
);

当使用区块编辑器时,此元数据框将不再在元数据框区域中显示,因为它现在仅用于向后兼容目的。它将在经典编辑器中像以前一样显示。

元数据框数据收集

在每个区块编辑器页面加载时,我们注册一个操作来收集元数据框数据以确定某个区域是否为空。在收集元数据框数据后,原始全局状态将被重置。

请参阅 register_and_do_post_meta_boxes。

它将遍历 post.php 运行以注册元数据框的函数和钩子;即 add_meta_boxes、add_meta_boxes_{$post->post_type} 和 do_meta_boxes。

元数据框会被过滤以移除任何核心元数据框、标准自定义分类法元数据框以及任何声明自己仅用于向后兼容目的的元数据框。

然后检查此特定类型元数据框的每个位置是否处于活动状态。如果它不为空,则存储 true 值;如果为空,则存储 false 值。然后将此元数据框位置数据由编辑器 Redux 商店在 INITIALIZE_META_BOX_STATE 中分发。

理想情况下,这可以在编辑器实例化时完成并有助于简化此流程。但是,在调用 initializeEditor() 的 admin_enqueue_scripts 之前无法知道元数据框状态。这必须如此,除非我们想将 initializeEditor() 移至在页脚或 admin_head 之后的某个时间点触发。随着对编辑器引导程序的最近更改,这可能现在成为可能。请与 ACF 进行测试以确保。

Redux 和 React 元数据框管理

在渲染区块编辑器时,元数据框被渲染到隐藏 div #metaboxes 中。

Redux 商店默认将所有元数据框保持为非活动状态。当
INITIALIZE_META_BOX_STATE 到来时,商店将通过将 isActive 标志设置为 true 来更新任何活动的元数据框区域。一旦发生这种情况,React 将检查 Redux 在 MetaBox 组件上发送的新属性。如果该 MetaBox 现在处于活动状态,而不是渲染 null,将渲染一个 MetaBoxArea 组件。MetaBox 组件是介于 MetaBoxArea 和 Redux 商店之间的容器组件。如果没有元数据框处于活动状态,则不会发生任何事情。这将是默认行为,因为所有核心元数据框都已被移除。

MetaBoxArea 组件

当组件渲染时,它将存储对元数据框容器的引用并从预取位置检索元数据框 HTML。

当帖子更新时,只有活动的元数据框区域才会被提交。这可以防止不必要的请求。元数据框提交不会创建额外的修订版。Redux 操作将在 REQUEST_POST_UPDATE 上触发任何活动的元数据框。请参阅 editor/effects.js。REQUEST_META_BOX_UPDATES 操作将将该元数据框的状态设置为 isUpdating。isUpdating 属性将被发送到 MetaBoxArea 并导致表单提交。

当元数据框区域保存时,我们显示一个更新覆盖层,以防止用户在保存过程中更改表单值。

示例保存 URL 如下所示:

example.org/wp-admin/post.php?post=1&action=edit&meta-box-loader=1

此 URL 会自动通过 _wpMetaBoxUrl 全局变量传递给 React。

此页面模仿 post.php 帖子表单,因此当它被提交时,它将触发所有正常的钩子和操作,并具有正确的全局状态以在不修改任何现有代码的情况下正确触发任何 PHP 元数据框 mumbo jumbo。在成功提交后,React 将发出一个 handleMetaBoxReload 以移除更新覆盖层。

常见兼容性问题

大多数 PHP 元数据框应在区块编辑器中继续工作,但一些包含高级功能的元数据框可能会损坏。以下是元数据框在区块编辑器中可能无法按预期工作的常见原因:

  • 依赖选择器以针对帖子标题、帖子内容字段和其他元数据框(旧编辑器的)的插件。
  • 依赖 TinyMCE 的 API 的插件,因为区块编辑器中不再有单个 TinyMCE 实例与之通信。
  • 在“提交”或“保存”时对其 DOM 进行更新的插件。

请注意,如果您的插件触发 PHP 警告或通知输出到页面,这将导致 HTML 文档类型(<!DOCTYPE html>)被错误输出。这将导致浏览器使用“怪异模式”进行渲染,这是一种当浏览器不知道它正在解析的文档类型时启用的兼容层。区块编辑器不打算在此模式下工作,但它可能 看起来 工作正常。如果您遇到诸如 元数据框覆盖编辑器 或其他布局问题等问题,请检查您文档的原始页面源代码以查看文档类型定义是否是页面上输出的第一件事。JavaScript 控制台也将有一个警告,指出该问题。

其他资源