分析 `register_block_type()` 函数的源码,它是如何注册古腾堡(Gutenberg)区块的?

各位同学,早上好!今天咱们聊聊古腾堡,也就是WordPress编辑器里那些神奇的“积木”——区块。更具体地说,我们要扒一扒 register_block_type() 这个函数,看看它是如何把这些“积木”注册到WordPress里的。

开场白:为什么我们要关心这个函数?

想象一下,你是个乐高设计师,register_block_type() 就是你的“乐高零件注册表”。你设计了一个新的零件(区块),要让所有人都知道它,并且能用它来搭建各种模型(页面),你就必须把这个零件的信息登记到这个注册表里。

好,废话不多说,咱们直接上代码!

register_block_type() 的基本结构

register_block_type() 函数接收两个参数:

  1. $block_type: 区块的名称,字符串类型。这个名字要遵循特定的格式(稍后会详细解释)。
  2. $settings: 一个数组,包含了区块的所有配置信息,比如编辑器里显示的属性、渲染方式等等。
<?php
/**
 * Registers a block type.
 *
 * @since 5.0.0
 *
 * @param string|WP_Block_Type $block_type Block type name including namespace.
 *                                        In general, names should not contain
 *                                        uppercase characters or hyphens.
 *                                        Allowed characters are lowercase letters, numbers,
 *                                        and underscores. Spaces are not allowed.
 * @param array $settings Block type settings array.
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
 */
function register_block_type( $block_type, $settings = array() ) {
    // 代码省略...
}
?>

深入剖析 $block_type:区块的“身份证”

$block_type 参数是区块的唯一标识符,就像是你的身份证号码一样。它必须遵循一定的命名规范,确保WordPress能正确识别你的区块。

  • 格式: namespace/block-name
    • namespace: 通常是你的主题或插件的名称,用于避免和其他区块冲突。
    • block-name: 区块的具体名称,描述区块的功能。
  • 命名规则:
    • 只能包含小写字母、数字和下划线。
    • 不能包含大写字母或连字符。
    • 不能包含空格。

举个栗子:my-theme/fancy-buttonmy-plugin/image-slider都是合法的区块名称。

$settings:区块的“说明书”

$settings 参数是一个数组,它定义了区块的所有属性和行为。这个数组里的内容非常丰富,我们逐一来看:

属性 类型 描述
title string 区块的标题,会在编辑器里显示。
description string 区块的描述,用于解释区块的用途。
icon string 区块的图标,会在编辑器里显示。可以是Dashicons的名称,也可以是自定义的SVG。
category string 区块的分类,用于在编辑器里组织区块。常用的分类有commonformattinglayoutwidgetsembed
attributes array 区块的属性,定义了区块可以接受哪些数据。每个属性都有自己的类型、默认值等。
supports array 区块支持的功能,比如对齐方式、HTML锚点等。
render_callback callable 一个回调函数,用于在前端渲染区块。这个函数会接收区块的属性作为参数,并返回HTML代码。
render string (已弃用,推荐使用render_callback)一个PHP文件路径,用于在前端渲染区块。
editor_script string 一个JavaScript文件路径,用于定义区块在编辑器里的行为。
editor_style string 一个CSS文件路径,用于定义区块在编辑器里的样式。
style string 一个CSS文件路径,用于定义区块在前端的样式。

attributes:区块的“数据仓库”

attributes 属性是区块的核心,它定义了区块可以存储和处理哪些数据。每个属性都有自己的名称、类型和默认值。

'attributes' => array(
    'title' => array(
        'type' => 'string',
        'default' => '默认标题',
    ),
    'content' => array(
        'type' => 'string',
    ),
    'imageUrl' => array(
        'type' => 'string',
        'default' => '',
    ),
    'alignment' => array(
        'type' => 'string',
        'default' => 'left',
    ),
)

在这个例子中,我们定义了四个属性:

  • title:字符串类型,默认值为“默认标题”。
  • content:字符串类型,没有默认值。
  • imageUrl:字符串类型,默认值为空字符串。
  • alignment:字符串类型,默认值为“left”。

常用的属性类型包括:

  • string:字符串
  • number:数字
  • boolean:布尔值(true或false)
  • array:数组
  • object:对象

render_callback:区块的“演员”

render_callback 属性是一个回调函数,它负责在前端渲染区块。这个函数会接收区块的属性作为参数,并返回HTML代码。

'render_callback' => 'my_theme_render_fancy_button',
<?php
function my_theme_render_fancy_button( $attributes ) {
    $title = isset( $attributes['title'] ) ? $attributes['title'] : '默认标题';
    $alignment = isset( $attributes['alignment'] ) ? $attributes['alignment'] : 'left';

    $html = '<div class="fancy-button" style="text-align: ' . esc_attr( $alignment ) . ';">';
    $html .= '<button>' . esc_html( $title ) . '</button>';
    $html .= '</div>';

    return $html;
}
?>

在这个例子中,my_theme_render_fancy_button 函数接收一个 $attributes 数组,包含了区块的所有属性。我们从数组中取出 titlealignment 属性,并根据这些属性生成HTML代码。

editor_scripteditor_style:区块的“化妆师”和“造型师”

editor_script 属性是一个JavaScript文件路径,用于定义区块在编辑器里的行为。你可以在这个文件中编写JavaScript代码,控制区块的编辑界面、处理用户输入等等。

editor_style 属性是一个CSS文件路径,用于定义区块在编辑器里的样式。你可以使用CSS来美化区块在编辑器里的显示效果,让用户更容易识别和操作区块。

一个完整的例子:注册一个简单的文本区块

<?php
add_action( 'init', 'my_theme_register_text_block' );

function my_theme_register_text_block() {
    register_block_type(
        'my-theme/text-block',
        array(
            'title' => __( '文本区块', 'my-theme' ),
            'description' => __( '一个简单的文本区块', 'my-theme' ),
            'icon' => 'edit',
            'category' => 'common',
            'attributes' => array(
                'content' => array(
                    'type' => 'string',
                    'default' => '默认文本',
                ),
                'textColor' => array(
                    'type' => 'string',
                    'default' => 'black',
                ),
            ),
            'supports' => array(
                'align' => true, // 支持对齐方式
            ),
            'render_callback' => 'my_theme_render_text_block',
            'editor_script' => 'my-theme-text-block-editor-script',
            'editor_style' => 'my-theme-text-block-editor-style',
            'style' => 'my-theme-text-block-style',
        )
    );
}

function my_theme_render_text_block( $attributes ) {
    $content = isset( $attributes['content'] ) ? $attributes['content'] : '默认文本';
    $textColor = isset( $attributes['textColor'] ) ? $attributes['textColor'] : 'black';
    $alignment = isset( $attributes['align'] ) ? $attributes['align'] : 'left';

    $html = '<div class="text-block" style="color: ' . esc_attr( $textColor ) . '; text-align: ' . esc_attr( $alignment ). ';">';
    $html .= esc_html( $content );
    $html .= '</div>';

    return $html;
}

//enqueue scripts and styles
add_action( 'enqueue_block_editor_assets', 'my_theme_enqueue_text_block_editor_assets' );
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_text_block_assets' );

function my_theme_enqueue_text_block_editor_assets() {
    wp_enqueue_script(
        'my-theme-text-block-editor-script',
        get_template_directory_uri() . '/assets/js/text-block-editor.js',
        array( 'wp-blocks', 'wp-element', 'wp-editor' ),
        filemtime( get_template_directory() . '/assets/js/text-block-editor.js' )
    );

    wp_enqueue_style(
        'my-theme-text-block-editor-style',
        get_template_directory_uri() . '/assets/css/text-block-editor.css',
        array( 'wp-edit-blocks' ),
        filemtime( get_template_directory() . '/assets/css/text-block-editor.css' )
    );
}

function my_theme_enqueue_text_block_assets() {
    wp_enqueue_style(
        'my-theme-text-block-style',
        get_template_directory_uri() . '/assets/css/text-block.css',
        array(),
        filemtime( get_template_directory() . '/assets/css/text-block.css' )
    );
}
?>

JavaScript (assets/js/text-block-editor.js):

wp.blocks.registerBlockType('my-theme/text-block', {
    edit: function(props) {
        const { attributes, setAttributes } = props;
        const { content, textColor } = attributes;

        function onChangeContent(newValue) {
            setAttributes({ content: newValue });
        }

        function onChangeTextColor(newValue) {
            setAttributes({ textColor: newValue });
        }

        return React.createElement(
            'div',
            { className: props.className },
            React.createElement('p', null, 'Text Block:'),
            React.createElement('input', {
                type: 'text',
                value: content,
                onChange: (event) => onChangeContent(event.target.value),
            }),
            React.createElement('input', {
                type: 'color',
                value: textColor,
                onChange: (event) => onChangeTextColor(event.target.value),
            })
        );
    },
    save: function(props) {
        return null; // Rendered server-side using render_callback
    },
});

CSS (assets/css/text-block-editor.css):

.wp-block-my-theme-text-block {
  border: 1px solid #ccc;
  padding: 10px;
}

CSS (assets/css/text-block.css):

.text-block {
  margin-bottom: 15px;
}

这个例子注册了一个名为 my-theme/text-block 的区块,它允许用户输入文本内容,并选择文本颜色。

代码解释:

  1. add_action( 'init', 'my_theme_register_text_block' );: 在 WordPress 初始化时,调用 my_theme_register_text_block 函数。
  2. register_block_type(...): 注册区块,传入区块名称和配置信息。
  3. attributes: 定义了两个属性:content (文本内容) 和 textColor (文本颜色),都为字符串类型。
  4. render_callback: 指定了 my_theme_render_text_block 函数来渲染区块。
  5. my_theme_render_text_block(...): 接收区块属性,生成包含文本内容的 HTML 代码。
  6. enqueue_block_editor_assets and wp_enqueue_scripts: Enqueue javascript for the editor and css for the front-end.
  7. JavaScript (text-block-editor.js): Defines the visual editing interface.
  8. CSS (text-block-editor.css and text-block.css): Provides styles for editor and front-end.

总结:register_block_type() 的工作流程

  1. 你调用 register_block_type() 函数,传入区块名称和配置信息。
  2. WordPress 会验证区块名称是否符合规范。
  3. WordPress 会将区块的配置信息存储到数据库中。
  4. 当用户在编辑器里使用你的区块时,WordPress 会加载你的 JavaScript 和 CSS 文件,并调用你的 render_callback 函数来渲染区块。

一些补充说明:

  • 区块的存储方式: 区块的数据存储在文章的内容里,使用一种叫做“区块语法”的格式。这种格式类似于HTML注释,但包含了区块的名称和属性。
  • 动态区块 vs 静态区块: 上面的例子是一个动态区块,因为它的内容是在服务器端动态生成的。你也可以创建静态区块,它的内容完全在客户端生成,不需要服务器端渲染。
  • 区块的进阶用法: 你可以使用JavaScript API来创建更复杂的区块,比如包含交互式组件、使用REST API获取数据等等。

结尾:成为古腾堡大师的道路

register_block_type() 函数是古腾堡区块开发的基石。掌握了这个函数,你就可以开始创建自己的区块,扩展WordPress编辑器的功能。当然,这只是一个开始,古腾堡的世界还有很多值得探索的地方。希望今天的讲解能帮助你入门,祝你在古腾堡的道路上越走越远!

今天的讲座就到这里,谢谢大家!有没有什么问题?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注