阐述 WordPress `register_block_type()` 函数的源码:如何定义区块的属性、渲染回调和脚本。

好嘞!各位观众老爷,欢迎来到“区块大爆炸”现场!今天咱们就来扒一扒 WordPress 区块注册的 “葵花宝典” —— register_block_type()。 准备好迎接信息的狂轰滥炸了吗?Let’s go!

开场白:区块的世界,从注册开始

在 WordPress 的世界里,区块就像一个个乐高积木,可以自由组合,搭建出各种炫酷的页面。而 register_block_type() 函数,就是把这些积木“注册”到 WordPress 系统的“户口登记处”,让它们合法存在,可以被 WordPress 编辑器识别和使用。

register_block_type() 的庐山真面目

register_block_type() 函数的基本用法如下:

register_block_type( string $block_name, array|string $settings );
  • $block_name: 区块的名称,必须是唯一的,通常采用 namespace/block-name 的格式,比如 my-plugin/awesome-block
  • $settings: 一个数组,包含了区块的所有配置信息,比如属性、渲染回调、编辑器脚本等等。

今天咱们重点就来解剖一下这个 $settings 数组,看看里面都藏着哪些乾坤。

$settings 数组:区块的灵魂所在

$settings 数组是区块定义的核心,它决定了区块的行为和外观。我们来逐个击破它的关键成员:

  1. attributes:区块的百变造型

    attributes 定义了区块的属性,属性就像区块的“变量”,可以存储各种数据,比如文本、颜色、图片 URL 等等。用户可以在编辑器中修改这些属性的值,从而改变区块的显示效果。

    attributes 是一个关联数组,键是属性的名称,值是属性的配置信息。每个属性的配置信息又是一个数组,包含以下几个关键字段:

    • type: 属性的类型,可以是 stringnumberbooleanarrayobject
    • default: 属性的默认值。
    • source: 属性值的来源,比如从 HTML 属性、文本内容、内部区块等获取。
    • selector: CSS 选择器,用于从 HTML 中提取属性值。

    举个栗子,咱们定义一个简单的标题区块,包含 titlelevel 两个属性:

    $settings = array(
        'attributes' => array(
            'title' => array(
                'type' => 'string',
                'default' => '默认标题',
            ),
            'level' => array(
                'type' => 'number',
                'default' => 2,
            ),
        ),
        // ... 其他配置
    );

    在这个例子中,title 属性的类型是 string,默认值是 '默认标题'level 属性的类型是 number,默认值是 2

    source 的妙用

    source 字段可以指定属性值的来源。常用的 source 类型包括:

    • attribute: 从 HTML 元素的属性中获取。例如:

      'imageUrl' => array(
          'type' => 'string',
          'source' => 'attribute',
          'attribute' => 'src', // 从 <img> 元素的 src 属性获取
          'selector' => 'img', // 匹配 <img> 元素
      ),
    • text: 从 HTML 元素的文本内容中获取。例如:

      'content' => array(
          'type' => 'string',
          'source' => 'text',
          'selector' => 'p', // 匹配 <p> 元素
      ),
    • html: 从 HTML 元素的内部 HTML 中获取。 例如:

      'content' => array(
          'type' => 'string',
          'source' => 'html',
          'selector' => '.my-block-content', // 匹配 class 为 my-block-content 的元素
       ),
    • children: 获取内部区块。例如:

      'blocks' => array(
         'type' => 'array',
         'source' => 'children',
      ),
    • query: 获取多个匹配元素的属性。例如:

       'items' => array(
          'type' => 'array',
          'source' => 'query',
          'selector' => 'li',
          'query' => array(
              'text' => array(
                  'type' => 'string',
                  'source' => 'text',
              ),
          ),
       ),

    selector 字段用于指定要匹配的 HTML 元素。

    表格总结:attributes 的核心字段

    字段 类型 描述
    type string 属性的类型,可以是 stringnumberbooleanarrayobject
    default mixed 属性的默认值。
    source string 属性值的来源,比如 attributetexthtmlchildrenquery
    attribute string sourceattribute 时,指定要获取的 HTML 属性名称。
    selector string CSS 选择器,用于从 HTML 中提取属性值。
    query array sourcequery 时,指定要获取的多个元素的属性。
  2. render_callback:区块的灵魂画师

    render_callback 是一个回调函数,用于生成区块在前端显示的 HTML 代码。它接收一个 $attributes 数组作为参数,包含了区块的属性值。

    $settings = array(
        'attributes' => array( /* ... */ ),
        'render_callback' => 'my_block_render_callback',
    );
    
    function my_block_render_callback( $attributes ) {
        $title = isset( $attributes['title'] ) ? $attributes['title'] : '默认标题';
        $level = isset( $attributes['level'] ) ? $attributes['level'] : 2;
        return '<h' . $level . '>' . esc_html( $title ) . '</h' . $level . '>';
    }

    在这个例子中,my_block_render_callback() 函数根据 titlelevel 属性的值,生成一个标题 HTML 元素。

    注意事项:

    • render_callback 必须返回一个字符串,表示区块的 HTML 代码。
    • render_callback 中,一定要对用户输入进行转义,防止 XSS 攻击。可以使用 esc_html()esc_attr() 等函数进行转义。
    • 如果区块不需要在前端显示,可以不定义 render_callback
  3. editor_scriptscript:区块的左右护法

    • editor_script: 指定区块在编辑器中使用的 JavaScript 文件。这个文件通常包含区块的编辑逻辑,比如自定义控件、数据验证等等。
    • script: 指定区块在前端使用的 JavaScript 文件。这个文件通常包含区块的交互逻辑,比如动画效果、AJAX 请求等等。
    $settings = array(
        'attributes' => array( /* ... */ ),
        'render_callback' => 'my_block_render_callback',
        'editor_script' => 'my-plugin-block-editor-script',
        'script' => 'my-plugin-block-script',
    );

    在这个例子中,my-plugin-block-editor-script 是编辑器脚本的句柄,my-plugin-block-script 是前端脚本的句柄。

    如何注册脚本?

    在使用 editor_scriptscript 之前,需要先使用 wp_register_script() 函数注册脚本。

    function my_plugin_register_block() {
        wp_register_script(
            'my-plugin-block-editor-script',
            plugins_url( 'block-editor.js', __FILE__ ),
            array( 'wp-blocks', 'wp-element', 'wp-editor' ),
            filemtime( plugin_dir_path( __FILE__ ) . 'block-editor.js' )
        );
    
        wp_register_script(
            'my-plugin-block-script',
            plugins_url( 'block.js', __FILE__ ),
            array( 'wp-element' ),
            filemtime( plugin_dir_path( __FILE__ ) . 'block.js' )
        );
    
        register_block_type( 'my-plugin/my-block', $settings );
    }
    add_action( 'init', 'my_plugin_register_block' );
    • plugins_url( 'block-editor.js', __FILE__ ): 获取脚本文件的 URL。
    • array( 'wp-blocks', 'wp-element', 'wp-editor' ): 指定脚本的依赖项。
    • filemtime( plugin_dir_path( __FILE__ ) . 'block-editor.js' ): 获取脚本文件的修改时间,用于缓存刷新。

    editor_scriptscript 的区别

    特性 editor_script script
    加载环境 WordPress 编辑器 前端页面
    主要用途 定义区块的编辑界面、自定义控件、数据验证等。 定义区块的交互逻辑、动画效果、AJAX 请求等。
    常用依赖项 wp-blocks, wp-element, wp-editor, wp-components 等 WordPress 组件。 wp-element 等 WordPress 组件,以及第三方 JavaScript 库(如 jQuery)。
    是否必须 不是必须的,如果区块不需要自定义编辑界面,可以不定义。 不是必须的,如果区块不需要交互逻辑,可以不定义。
  4. editor_stylestyle:区块的化妆师

    • editor_style: 指定区块在编辑器中使用的 CSS 样式表。
    • style: 指定区块在前端使用的 CSS 样式表。
    $settings = array(
        'attributes' => array( /* ... */ ),
        'render_callback' => 'my_block_render_callback',
        'editor_script' => 'my-plugin-block-editor-script',
        'script' => 'my-plugin-block-script',
        'editor_style' => 'my-plugin-block-editor-style',
        'style' => 'my-plugin-block-style',
    );

    如何注册样式表?

    与注册脚本类似,需要先使用 wp_register_style() 函数注册样式表。

    function my_plugin_register_block() {
        wp_register_style(
            'my-plugin-block-editor-style',
            plugins_url( 'block-editor.css', __FILE__ ),
            array( 'wp-edit-blocks' ),
            filemtime( plugin_dir_path( __FILE__ ) . 'block-editor.css' )
        );
    
        wp_register_style(
            'my-plugin-block-style',
            plugins_url( 'block.css', __FILE__ ),
            array( 'wp-block-library' ),
            filemtime( plugin_dir_path( __FILE__ ) . 'block.css' )
        );
    
        register_block_type( 'my-plugin/my-block', $settings );
    }
    add_action( 'init', 'my_plugin_register_block' );

    editor_stylestyle 的区别

    特性 editor_style style
    加载环境 WordPress 编辑器 前端页面
    主要用途 定义区块在编辑器中的显示样式。 定义区块在前端页面的显示样式。
    常用依赖项 wp-edit-blocks 等 WordPress 样式表。 wp-block-library 等 WordPress 样式表。
    是否必须 不是必须的,如果区块使用主题的默认样式,可以不定义。 不是必须的,如果区块使用主题的默认样式,可以不定义。
  5. supports:区块的超能力

    supports 数组用于声明区块支持的特性,比如对齐方式、HTML 锚点、自定义类名等等。

    $settings = array(
        'attributes' => array( /* ... */ ),
        'render_callback' => 'my_block_render_callback',
        'editor_script' => 'my-plugin-block-editor-script',
        'script' => 'my-plugin-block-script',
        'supports' => array(
            'align' => true, // 支持对齐方式
            'anchor' => true, // 支持 HTML 锚点
            'className' => false, // 禁用自定义类名
            'html' => false, // 禁用 HTML 编辑
        ),
    );

    常用的 supports 选项:

    选项 类型 描述
    align boolean 是否支持对齐方式。如果设置为 true,用户可以在编辑器中选择区块的对齐方式(左对齐、居中对齐、右对齐、宽对齐、全宽对齐)。
    anchor boolean 是否支持 HTML 锚点。如果设置为 true,用户可以为区块添加一个唯一的 HTML 锚点,用于页面内部链接。
    className boolean 是否支持自定义 CSS 类名。如果设置为 true,用户可以为区块添加自定义的 CSS 类名。如果设置为 false,则禁用自定义类名,只允许使用 WordPress 提供的默认类名。
    html boolean 是否允许用户直接编辑区块的 HTML 代码。如果设置为 false,则禁用 HTML 编辑,只允许通过编辑器界面修改区块的内容。这可以提高安全性,防止用户插入恶意代码。
    multiple boolean 是否允许在同一篇文章中插入多个相同的区块。如果设置为 false,则只允许插入一个。
    inserter boolean 控制是否在区块插入器中显示该区块。 默认为true。 设置为false可以隐藏区块。
    color boolean/array 控制区块是否支持颜色设置。 可以设置为true启用默认颜色设置,或者设置为一个数组来配置具体的颜色选项,例如支持背景色、文本颜色等。
    spacing array 控制区块是否支持间距设置,如 padding 和 margin。 你可以配置支持的具体属性和单位。
    typography array 控制区块是否支持排版设置,如字体大小、字体粗细、行高等。 你可以配置支持的具体属性和单位。
  6. parentprovides_contextuses_context: 区块间的亲密关系

    • parent: 指定区块的父区块,用于创建嵌套区块。
    • provides_context: 指定区块提供的上下文信息,供子区块使用。
    • uses_context: 指定区块使用的上下文信息,由父区块提供。

    这三个选项用于实现区块之间的通信和数据共享。

    例如,我们可以创建一个包含多个子标题的章节区块:

    // 注册章节区块
    register_block_type( 'my-plugin/chapter', array(
        'attributes' => array(
            'title' => array(
                'type' => 'string',
                'default' => '默认章节标题',
            ),
        ),
        'provides_context' => array(
            'my-plugin/chapterTitle' => 'title', // 提供章节标题作为上下文信息
        ),
        'render_callback' => 'my_chapter_render_callback',
    ) );
    
    // 注册子标题区块
    register_block_type( 'my-plugin/subheading', array(
        'attributes' => array(
            'content' => array(
                'type' => 'string',
                'default' => '默认子标题内容',
            ),
        ),
        'parent' => array( 'my-plugin/chapter' ), // 指定父区块为章节区块
        'uses_context' => array(
            'my-plugin/chapterTitle' => 'chapterTitle', // 使用章节标题上下文信息
        ),
        'render_callback' => 'my_subheading_render_callback',
    ) );
    
    function my_chapter_render_callback( $attributes, $content ) {
        $title = isset( $attributes['title'] ) ? $attributes['title'] : '默认章节标题';
        return '<div class="chapter"><h2>' . esc_html( $title ) . '</h2>' . $content . '</div>';
    }
    
    function my_subheading_render_callback( $attributes, $content, $block ) {
        $chapterTitle = $block->context['my-plugin/chapterTitle']; // 获取章节标题上下文信息
        $content = isset( $attributes['content'] ) ? $attributes['content'] : '默认子标题内容';
        return '<div class="subheading"><h3>' . esc_html( $content ) . ' (章节标题: ' . esc_html( $chapterTitle ) . ')</h3></div>';
    }

    在这个例子中,章节区块提供了 my-plugin/chapterTitle 上下文信息,子标题区块使用了这个上下文信息,从而可以在子标题中显示章节标题。

完整示例:一个简单的文本区块

咱们来一个完整的例子,创建一个简单的文本区块,包含一个 content 属性,允许用户编辑文本内容:

function my_plugin_register_block() {
    register_block_type( 'my-plugin/text-block', array(
        'attributes' => array(
            'content' => array(
                'type' => 'string',
                'default' => '请输入文本内容',
            ),
        ),
        'editor_script' => 'my-plugin-text-block-editor-script',
        'style' => 'my-plugin-text-block-style',
        'render_callback' => 'my_text_block_render_callback',
    ) );

    wp_register_script(
        'my-plugin-text-block-editor-script',
        plugins_url( 'text-block-editor.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element', 'wp-editor' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'text-block-editor.js' )
    );

    wp_register_style(
        'my-plugin-text-block-style',
        plugins_url( 'text-block.css', __FILE__ ),
        array( 'wp-block-library' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'text-block.css' )
    );
}
add_action( 'init', 'my_plugin_register_block' );

function my_text_block_render_callback( $attributes ) {
    $content = isset( $attributes['content'] ) ? $attributes['content'] : '请输入文本内容';
    return '<p>' . esc_html( $content ) . '</p>';
}

对应的 text-block-editor.js 文件内容(使用 React):

const { registerBlockType } = wp.blocks;
const { RichText } = wp.editor;

registerBlockType( 'my-plugin/text-block', {
    title: '文本区块',
    icon: 'edit',
    category: 'common',
    attributes: {
        content: {
            type: 'string',
            default: '请输入文本内容',
        },
    },
    edit: ( props ) => {
        const { attributes, setAttributes } = props;
        const { content } = attributes;

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

        return (
            <RichText
                tagName="p"
                className={ props.className }
                value={ content }
                onChange={ onChangeContent }
                placeholder="请输入文本内容"
            />
        );
    },
    save: ( props ) => {
        return (
            <p>
                { props.attributes.content }
            </p>
        );
    },
} );

对应的 text-block.css 文件内容:

.wp-block-my-plugin-text-block p {
    font-size: 16px;
    line-height: 1.5;
}

总结:掌握 register_block_type(),玩转区块世界

register_block_type() 函数是 WordPress 区块注册的核心。掌握了它的用法,你就可以自定义各种各样的区块,扩展 WordPress 的功能,打造出独一无二的网站。

希望今天的讲座对大家有所帮助!下次再见!

发表回复

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