分析 WordPress `register_block_type()` 函数的源码:如何注册 Gutenberg 区块。

各位靓仔靓女们,今天咱们来聊聊WordPress里那个让人又爱又恨的Gutenberg编辑器,特别是它的核心武器之一:register_block_type()。 别怕,咱们不搞枯燥的源码解读,而是用大白话,加上一些“骚操作”,把这个函数扒个精光。

开场白:为什么你需要了解 register_block_type()

想象一下,你是个建筑师,想盖一座与众不同的房子。但你手头只有一些乐高积木,怎么办? 你需要设计自己的积木,告诉大家这些积木长啥样,怎么用。 register_block_type() 就是WordPress里设计你自己的“Gutenberg积木”的工具。 掌握它,你就掌握了Gutenberg的半壁江山,就能创造出各种炫酷的、定制化的区块,让你的网站内容不再千篇一律。

第一部分:register_block_type() 的基本结构

register_block_type() 顾名思义,就是“注册区块类型”的函数。 它的基本用法很简单:

<?php
/**
 * Registers a new block type.
 *
 * @param string|WP_Block_Type $block_name Block name. Must contain a namespace prefix.
 * @param array|string         $args       Array of block type arguments. Alternatively, a file path to a JSON schema definition of the block type.
 *
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
 */
function register_block_type( $block_name, $args ) {}

看到了吧,它接受两个参数:

  1. $block_name: 区块的名字,必须包含命名空间前缀。 就像你的乐高积木牌子,比如 'my-awesome-plugin/my-block'。 命名空间是为了防止和其他插件的区块重名,导致世界末日。
  2. $args: 一个数组,包含了区块的所有信息,比如长啥样,怎么用,有哪些属性等等。 也可以是一个指向JSON文件的路径,这个JSON文件定义了区块的所有信息。

简单例子,热热身:

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

function register_my_block() {
    register_block_type(
        'my-awesome-plugin/my-block',
        array(
            'attributes'      => array(
                'message' => array(
                    'type'    => 'string',
                    'default' => 'Hello Gutenberg!',
                ),
            ),
            'render_callback' => 'render_my_block',
        )
    );
}

function render_my_block( $attributes ) {
    $message = isset( $attributes['message'] ) ? $attributes['message'] : 'Hello Gutenberg!';
    return '<p>' . esc_html( $message ) . '</p>';
}
?>

这段代码做了什么?

  • add_action( 'init', 'register_my_block' ): 告诉WordPress,在初始化的时候,执行 register_my_block 函数。
  • register_my_block(): 注册了一个名为 'my-awesome-plugin/my-block' 的区块。
  • attributes: 定义了一个属性 message, 类型是字符串,默认值是 'Hello Gutenberg!'。 属性是区块可以接受的配置项,就像乐高积木的颜色、大小等等。
  • render_callback: 定义了一个函数 render_my_block,用来渲染区块的内容。 当你在编辑器里插入这个区块时,这个函数会被调用,生成最终的HTML代码。

render_my_block 函数接收一个 $attributes 参数,包含了区块的所有属性值。 我们从 $attributes 里取出 message 属性的值,然后用 esc_html() 函数进行转义,防止XSS攻击。 最后,返回一个包含 message 值的 <p> 标签。

第二部分:$args 数组的乾坤

$args 数组是 register_block_type() 的灵魂,它决定了区块的所有行为。 让我们来深入了解一下 $args 数组里可以放哪些东西:

键名 类型 描述
title string 区块的标题,会在编辑器里显示。
description string 区块的描述,也会在编辑器里显示,帮助用户了解区块的作用。
category string 区块的分类,决定了区块在编辑器里属于哪个分类。 WordPress 已经定义了一些常用的分类,比如 'common', 'formatting', 'layout', 'widgets', 'embed'
icon string/array 区块的图标,会在编辑器里显示。 可以是一个dashicon的名字,也可以是一个包含 src 属性的数组,指向一个SVG文件。
keywords array 区块的关键词,用户可以通过关键词搜索到这个区块。
attributes array 区块的属性定义,决定了区块可以接受哪些配置项。
supports array 区块支持的功能,比如是否支持对齐,是否支持HTML锚点等等。
example array 区块的示例数据,会在编辑器里显示,帮助用户了解区块的使用方法。
render_callback callable 用来渲染区块内容的函数。
editor_script string 编辑器使用的JavaScript脚本。
editor_style string 编辑器使用的CSS样式。
style string 前端使用的CSS样式。
view_script string 前端使用的JavaScript脚本。

attributes:区块的灵魂属性

attributes$args 数组里最重要的成员之一。 它定义了区块可以接受哪些配置项,以及这些配置项的类型、默认值等等。

attributes 的结构是一个关联数组,键名是属性的名字,键值是一个数组,包含了属性的详细信息。

<?php
'attributes' => array(
    'message' => array(
        'type'    => 'string',
        'default' => 'Hello Gutenberg!',
    ),
    'textColor' => array(
        'type'    => 'string',
        'default' => 'black',
    ),
    'fontSize' => array(
        'type'    => 'number',
        'default' => 16,
    ),
    'imageUrl' => array(
        'type'    => 'string',
        'default' => '',
    ),
    'alignment' => array(
        'type' => 'string',
        'enum' => array( 'left', 'center', 'right' ),
        'default' => 'left',
    ),
),
?>
  • type: 属性的类型,可以是 'string', 'number', 'boolean', 'array', 'object'
  • default: 属性的默认值。
  • enum: 属性的可选值,只有当 type'string' 时才有效。 就像一个下拉菜单,只能选择预定义的值。

render_callback:区块的渲染引擎

render_callback 是一个函数,用来渲染区块的内容。 当你在编辑器里插入这个区块时,这个函数会被调用,生成最终的HTML代码。

render_callback 函数接收一个 $attributes 参数,包含了区块的所有属性值。 你可以根据这些属性值,动态生成HTML代码。

<?php
function render_my_block( $attributes ) {
    $message   = isset( $attributes['message'] ) ? $attributes['message'] : 'Hello Gutenberg!';
    $textColor = isset( $attributes['textColor'] ) ? $attributes['textColor'] : 'black';
    $fontSize  = isset( $attributes['fontSize'] ) ? $attributes['fontSize'] : 16;
    $imageUrl  = isset( $attributes['imageUrl'] ) ? $attributes['imageUrl'] : '';
    $alignment = isset( $attributes['alignment'] ) ? $attributes['alignment'] : 'left';

    $style = "color: {$textColor}; font-size: {$fontSize}px; text-align: {$alignment};";

    $output = '<div style="' . esc_attr( $style ) . '">';

    if ( ! empty( $imageUrl ) ) {
        $output .= '<img src="' . esc_url( $imageUrl ) . '" alt="' . esc_attr( $message ) . '">';
    }

    $output .= '<p>' . esc_html( $message ) . '</p>';
    $output .= '</div>';

    return $output;
}
?>

这个 render_my_block 函数做了什么?

  • $attributes 里取出 message, textColor, fontSize, imageUrl, alignment 属性的值。
  • 根据这些属性值,生成一个CSS样式字符串。
  • 生成一个包含CSS样式的 <div> 标签。
  • 如果 imageUrl 属性不为空,就生成一个 <img> 标签。
  • 生成一个包含 message 值的 <p> 标签。
  • 把所有HTML代码拼接起来,返回。

第三部分:高级技巧与骚操作

掌握了基本用法,我们来玩点高级的,让你的区块更上一层楼。

1. 使用JSON文件定义区块

如果你觉得 $args 数组太长,难以维护,可以使用JSON文件来定义区块。

<?php
register_block_type( 'my-awesome-plugin/my-block', plugin_dir_path( __FILE__ ) . 'block.json' );
?>

block.json 文件的内容:

{
    "name": "my-awesome-plugin/my-block",
    "title": "My Awesome Block",
    "description": "A simple block with a message.",
    "category": "common",
    "icon": "smiley",
    "keywords": [ "message", "hello" ],
    "attributes": {
        "message": {
            "type": "string",
            "default": "Hello Gutenberg!"
        }
    },
    "supports": {
        "align": true
    },
    "render": "file:./render.php"
}

注意 render 属性,它可以指定一个PHP文件来渲染区块的内容。 就像 render_callback 函数一样,这个PHP文件会接收 $attributes 参数,并返回HTML代码。 file:./render.php 指定了相对于 block.json 文件的 render.php 文件。

2. 使用 editor_scripteditor_style 添加编辑器样式和脚本

editor_scripteditor_style 允许你为编辑器添加自定义的JavaScript脚本和CSS样式。 这可以让你在编辑器里实现更复杂的功能,比如自定义的控件、预览效果等等。

首先,你需要注册你的JavaScript脚本和CSS样式:

<?php
function my_plugin_register_block() {
    wp_register_script(
        'my-block-editor-script',
        plugins_url( 'block.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element', 'wp-editor' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'block.js' )
    );

    wp_register_style(
        'my-block-editor-style',
        plugins_url( 'editor.css', __FILE__ ),
        array( 'wp-edit-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
    );

    wp_register_style(
        'my-block-style',
        plugins_url( 'style.css', __FILE__ ),
        array(),
        filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
    );

    register_block_type( 'my-awesome-plugin/my-block', array(
        'editor_script' => 'my-block-editor-script',
        'editor_style'  => 'my-block-editor-style',
        'style'         => 'my-block-style',
        'attributes' => array(
            'message' => array(
                'type'    => 'string',
                'default' => 'Hello Gutenberg!',
            ),
        ),
        'render_callback' => 'render_my_block',
    ) );
}
add_action( 'init', 'my_plugin_register_block' );
?>

然后,在你的 block.js 文件里,你可以使用 wp.blocks, wp.element, wp.editor 等JavaScript API来操作区块。

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 * @see https://developer.wordpress.org/block-editor/developers/block-api/block-registration/
 */
( function( blocks, element, editor ) {
    var el = element.createElement;
    var RichText = editor.RichText;

    blocks.registerBlockType( 'my-awesome-plugin/my-block', {
        title: 'My Awesome Block',
        icon: 'smiley',
        category: 'common',
        attributes: {
            message: {
                type: 'string',
                default: 'Hello Gutenberg!',
            },
        },
        edit: function( props ) {
            var message = props.attributes.message;
            function onChangeContent( newMessage ) {
                props.setAttributes( { message: newMessage } );
            }

            return el(
                RichText,
                {
                    tagName: 'p',
                    className: props.className,
                    value: message,
                    onChange: onChangeContent,
                    placeholder: 'Enter your message here...',
                }
            );
        },
        save: function( props ) {
            return el(
                RichText.Content,
                {
                    tagName: 'p',
                    className: props.className,
                    value: props.attributes.message,
                }
            );
        },
    } );
} )( window.wp.blocks, window.wp.element, window.wp.editor );

这个 block.js 文件做了什么?

  • 使用 wp.blocks.registerBlockType() 函数注册区块。注意,这里的区块名必须和PHP代码里注册的区块名一致。
  • 定义 edit 函数,用来渲染编辑器的界面。 edit 函数接收一个 props 参数,包含了区块的属性值和一些有用的函数,比如 setAttributes(),用来更新属性值。
  • RichText 组件是WordPress提供的一个富文本编辑器组件,可以让你在编辑器里输入和编辑文本。
  • 定义 save 函数,用来保存区块的内容。 save 函数接收一个 props 参数,包含了区块的属性值。

3. 使用 supports 控制区块的功能

supports 允许你控制区块的功能,比如是否支持对齐,是否支持HTML锚点等等。

<?php
'supports' => array(
    'align'           => true, // 是否支持对齐
    'anchor'          => true, // 是否支持HTML锚点
    'html'            => false, // 是否允许用户编辑HTML代码
    'customClassName' => true, // 是否允许用户添加自定义的CSS类名
),
?>

总结:register_block_type() 的武功秘籍

register_block_type() 是Gutenberg区块开发的基石。 掌握它,你就能创造出各种各样的区块,满足你的各种需求。

记住,要灵活运用 $args 数组里的各种参数,特别是 attributes, render_callback, editor_script, editor_style, supports

多看官方文档,多尝试,多实践,你就能成为Gutenberg区块开发的高手!

今天的讲座就到这里,希望大家有所收获! 以后有机会再和大家分享更多关于WordPress开发的技巧。 祝大家编码愉快!

发表回复

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