各位朋友,早上好!欢迎来到今天的“WordPress 区块开发实战讲座”。今天咱们要聊的是一个非常方便的函数:register_block_type_from_metadata()
。这玩意儿就像一个魔法棒,能帮你从 block.json
文件里“嗖”的一下,自动注册一个 WordPress 区块。
咱们先来聊聊,为啥需要这么个东西?
过去的日子:手动注册区块的苦
在 register_block_type_from_metadata()
出现之前,注册一个区块,那叫一个“累觉不爱”。你得手动编写 PHP 代码,告诉 WordPress 区块的名字、属性、编辑和保存函数等等。就像这样:
<?php
function my_custom_block_init() {
register_block_type( 'my-plugin/my-block', array(
'attributes' => array(
'content' => array(
'type' => 'string',
'default' => 'Hello World!',
),
),
'render_callback' => 'my_custom_block_render',
) );
}
add_action( 'init', 'my_custom_block_init' );
function my_custom_block_render( $attributes ) {
$content = isset( $attributes['content'] ) ? $attributes['content'] : '';
return '<p>' . esc_html( $content ) . '</p>';
}
你看,光是定义一个简单的区块,就得写这么多代码。如果区块稍微复杂点,属性多了,渲染逻辑也复杂了,那代码量简直要爆炸。而且,这些代码散落在不同的地方,维护起来也很麻烦。
block.json
:区块定义的救星
block.json
的出现,简直就是黑暗中的一道曙光。它允许你用 JSON 格式来定义区块的所有信息,包括名称、属性、标题、描述、图标、关键词等等。
一个简单的 block.json
文件可能长这样:
{
"name": "my-plugin/my-block",
"title": "My Custom Block",
"description": "A simple custom block.",
"category": "common",
"icon": "smiley",
"keywords": [ "custom", "block" ],
"attributes": {
"content": {
"type": "string",
"default": "Hello World!"
}
},
"supports": {
"align": true
},
"textdomain": "my-plugin"
}
这个 block.json
文件,就包含了我们之前手动注册区块时需要的所有信息。而且,JSON 格式的可读性非常好,也方便维护。
register_block_type_from_metadata()
:自动化注册的利器
有了 block.json
文件,我们就可以使用 register_block_type_from_metadata()
函数,来自动注册区块了。这个函数会读取 block.json
文件,然后根据文件中的信息,自动调用 register_block_type()
函数来注册区块。
使用起来非常简单:
<?php
function my_custom_block_init() {
register_block_type_from_metadata( __DIR__ );
}
add_action( 'init', 'my_custom_block_init' );
只需要传入 block.json
文件所在的目录即可。__DIR__
是一个 PHP 魔术常量,表示当前文件的目录。
register_block_type_from_metadata()
源码解析
好了,铺垫了这么多,咱们终于要进入正题了。让我们一起来深入了解一下 register_block_type_from_metadata()
函数的源码,看看它是如何工作的。
register_block_type_from_metadata()
函数的定义位于 wp-includes/block-library.php
文件中。咱们来看一段简化后的代码(为了方便讲解,去掉了错误处理和一些细节):
<?php
function register_block_type_from_metadata( $path ) {
$metadata_file = trailingslashit( $path ) . 'block.json';
if ( ! file_exists( $metadata_file ) ) {
return false; // 或者抛出异常,这里简化处理
}
$metadata = json_decode( file_get_contents( $metadata_file ), true );
if ( ! is_array( $metadata ) ) {
return false; // 或者抛出异常,这里简化处理
}
// 确保 'name' 键存在
if ( ! isset( $metadata['name'] ) ) {
return false; // 或者抛出异常,这里简化处理
}
// 构建注册参数
$args = array();
// 处理 render 回调
if ( isset( $metadata['render'] ) ) { // 注意这里是 'render' 而不是 'render_callback'
$render_file = trailingslashit( $path ) . $metadata['render'];
if ( file_exists( $render_file ) ) {
$args['render_callback'] = function( $attributes, $content, $block ) use ( $render_file ) {
ob_start();
include( $render_file );
return ob_get_clean();
};
}
}
// 处理编辑脚本和样式
if ( isset( $metadata['editorScript'] ) ) {
$args['editor_script'] = $metadata['editorScript'];
}
if ( isset( $metadata['editorStyle'] ) ) {
$args['editor_style'] = $metadata['editorStyle'];
}
if ( isset( $metadata['style'] ) ) {
$args['style'] = $metadata['style'];
}
// 其他属性,直接从 metadata 复制
$keys_to_copy = array(
'attributes',
'supports',
'provides_context',
'uses_context',
'viewScript', //注意这里是 'viewScript' 而不是 'view_script'
'textdomain',
);
foreach ( $keys_to_copy as $key ) {
if ( isset( $metadata[ $key ] ) ) {
$args[ $key ] = $metadata[ $key ];
}
}
// 注册区块
register_block_type( $metadata['name'], $args );
return true;
}
咱们来一行一行地分析一下这段代码:
-
$metadata_file = trailingslashit( $path ) . 'block.json';
: 首先,它会根据传入的$path
参数,拼接出block.json
文件的完整路径。trailingslashit()
函数的作用是在路径末尾添加一个斜杠,确保路径的正确性。 -
if ( ! file_exists( $metadata_file ) ) { ... }
: 接下来,它会检查block.json
文件是否存在。如果不存在,就直接返回false
。 -
$metadata = json_decode( file_get_contents( $metadata_file ), true );
: 如果block.json
文件存在,它会读取文件的内容,并使用json_decode()
函数将其解码成一个 PHP 数组。true
参数表示将 JSON 对象解码成关联数组。 -
if ( ! is_array( $metadata ) ) { ... }
: 然后,它会检查解码后的$metadata
是否是一个数组。如果不是,就说明block.json
文件的格式有问题,直接返回false
。 -
if ( ! isset( $metadata['name'] ) ) { ... }
: 接下来,它会检查$metadata
数组中是否存在name
键。name
键是区块的名称,也是必须存在的。如果不存在,就返回false
。 -
$args = array();
: 创建一个空数组$args
,用于存储传递给register_block_type()
函数的参数。 -
if ( isset( $metadata['render'] ) ) { ... }
: 这里处理了render
属性。注意,block.json
里面写的是render
,函数内部处理的是render_callback
。 如果block.json
中定义了render
属性,它会认为这是一个 PHP 文件的路径,并使用include()
函数来加载该文件,并将其输出作为区块的渲染结果。 这里使用了一个匿名函数作为render_callback
,这个匿名函数会使用ob_start()
和ob_get_clean()
函数来捕获include()
函数的输出。 -
if ( isset( $metadata['editorScript'] ) ) { ... }
if ( isset( $metadata['editorStyle'] ) ) { ... }
if ( isset( $metadata['style'] ) ) { ... }
: 这三段代码分别处理了editorScript
、editorStyle
和style
属性。这些属性分别指定了区块的编辑脚本、编辑样式和前端样式。 -
$keys_to_copy = array( ... );
: 创建一个数组$keys_to_copy
,包含了需要从$metadata
数组中复制到$args
数组中的键名。这些键名包括attributes
、supports
、provides_context
、uses_context
和textdomain
。 -
foreach ( $keys_to_copy as $key ) { ... }
: 遍历$keys_to_copy
数组,将$metadata
数组中对应的键值复制到$args
数组中。 -
register_block_type( $metadata['name'], $args );
: 最后,调用register_block_type()
函数,注册区块。$metadata['name']
是区块的名称,$args
是包含了区块各种属性的数组。 -
return true;
: 注册成功,返回true
。
block.json
属性与 register_block_type()
参数的对应关系
咱们来整理一下 block.json
文件中的属性与 register_block_type()
函数参数的对应关系:
block.json 属性 |
register_block_type() 参数 |
说明 |
---|---|---|
name |
(required) 区块名称 | 区块的唯一标识符,必须是 namespace/block-name 的格式。 |
title |
区块的标题,显示在区块编辑器中。 | |
description |
区块的描述,显示在区块编辑器中。 | |
category |
区块的分类,用于在区块编辑器中组织区块。 | |
icon |
区块的图标,显示在区块编辑器中。 | |
keywords |
区块的关键词,用于在区块编辑器中搜索区块。 | |
attributes |
attributes |
区块的属性,用于存储区块的数据。 |
supports |
supports |
区块支持的功能,例如对齐方式、颜色、字体大小等等。 |
provides_context |
provides_context |
区块提供的上下文,用于与其他区块共享数据。 |
uses_context |
uses_context |
区块使用的上下文,用于从其他区块获取数据。 |
render |
render_callback |
一个 PHP 文件的路径,用于渲染区块的前端内容。 |
editorScript |
editor_script |
区块的编辑脚本,用于在区块编辑器中控制区块的行为。 |
editorStyle |
editor_style |
区块的编辑样式,用于在区块编辑器中控制区块的外观。 |
style |
style |
区块的前端样式,用于在前端页面中控制区块的外观。 |
textdomain |
textdomain |
区块的文本域,用于国际化和本地化。 |
viewScript |
view_script |
用于增强区块前端行为的脚本。注意在block.json 中使用的名称是viewScript ,而在register_block_type() 中,正确的参数名称是 view_script 。 这个脚本通常用于添加交互性或动态功能,仅在区块的前端渲染时加载。 |
注意事项
render
vsrender_callback
:block.json
中使用的是render
属性,但在register_block_type()
函数中,对应的参数是render_callback
。register_block_type_from_metadata()
函数内部会处理这个转换。- 路径问题:
render
、editorScript
、editorStyle
和style
属性的值,都应该是相对于block.json
文件所在目录的路径。 - 错误处理: 在实际开发中,你需要添加更完善的错误处理机制,例如检查文件是否存在、JSON 格式是否正确等等。
- 安全性: 如果你的
render
文件中包含了用户输入的数据,一定要进行适当的转义,防止 XSS 攻击。 viewScript
属性:请注意在block.json
中使用的名称是viewScript
,而在register_block_type()
中,正确的参数名称是view_script
。
实战演练
咱们来做一个简单的实战演练,创建一个自定义区块,显示一段文本。
-
创建
block.json
文件:{ "name": "my-plugin/text-block", "title": "Text Block", "description": "A simple block to display text.", "category": "common", "icon": "text", "attributes": { "text": { "type": "string", "default": "Hello World!" } }, "editorScript": "build/index.js", "style": "build/style-index.css", "viewScript": "build/view.js" }
-
创建
index.js
文件 (编辑脚本):import { registerBlockType } from '@wordpress/blocks'; import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType( 'my-plugin/text-block', { edit: ( { attributes, setAttributes } ) => { const { text } = attributes; return ( <RichText { ...useBlockProps() } tagName="p" value={ text } onChange={ ( newText ) => setAttributes( { text: newText } ) } placeholder="Enter text here..." /> ); }, save: ( { attributes } ) => { const { text } = attributes; return ( <p { ...useBlockProps.save() }> { text } </p> ); }, } );
-
创建
style-index.css
文件 (前端样式):.wp-block-my-plugin-text-block { font-size: 16px; color: #333; }
-
创建
view.js
文件 (前端增强脚本):// view.js document.addEventListener('DOMContentLoaded', function() { const textBlocks = document.querySelectorAll('.wp-block-my-plugin-text-block'); textBlocks.forEach(block => { block.addEventListener('click', function() { alert('You clicked the text block!'); }); }); });
-
创建 PHP 文件来注册区块:
<?php function my_custom_block_init() { register_block_type_from_metadata( plugin_dir_path( __FILE__ ) ); } add_action( 'init', 'my_custom_block_init' );
-
构建脚本:
确保你已经安装了
@wordpress/scripts
。如果没有,请运行:npm install @wordpress/scripts --save-dev
然后在你的
package.json
文件中添加以下脚本:{ "scripts": { "build": "wp-scripts build" } }
然后运行:
npm run build
这会在你的区块目录中创建一个
build
目录,其中包含编译后的 JavaScript 和 CSS 文件。 -
激活插件: 将包含这些文件的文件夹打包成一个 WordPress 插件,并激活它。
现在,你就可以在 WordPress 编辑器中使用这个自定义区块了。
总结
register_block_type_from_metadata()
函数是一个非常方便的工具,可以帮助你从 block.json
文件中自动注册 WordPress 区块。它简化了区块注册的过程,提高了开发效率,也让代码更加清晰易懂。 掌握了这个函数,你就可以更加轻松地开发自定义区块,扩展 WordPress 的功能。
希望今天的讲座对你有所帮助! 感谢大家的聆听,祝大家编程愉快!