博客

掌握 WordPress 动态区块:融合 PHP 和 JavaScript 以实现交互式内容

了解如何创建动态 WordPress 区块,这些区块利用 PHP 和 JavaScript 为用户提供交互式和数据驱动的内容体验。

摘要

WordPress 动态区块提供了一种强大的方式来创建超越静态 HTML 的内容,从而实现实时数据集成和交互式元素。与静态区块不同,动态区块使用 PHP 在服务器端渲染其输出,使它们能够获取并显示最新的信息。本文提供了一个开发自己的动态区块的实用指南,涵盖了区块注册、服务器端渲染回调和客户端属性处理的基本概念。我们将通过一个分步示例,演示如何构建一个显示最新帖子的动态区块,为更复杂的交互式组件奠定基础。

掌握 WordPress 动态区块:融合 PHP 和 JavaScript 以实现交互式内容

WordPress 本身是一个强大的内容管理系统 (CMS),建立在模块化的 PHP 架构和 MySQL 数据库之上。其可扩展性是其主要优势,主要通过主题和插件实现。Gutenberg(区块编辑器)的出现通过引入模块化的、由 JavaScript 驱动的界面,彻底改变了内容创建。虽然静态 Gutenberg 区块非常适合结构化、不变的内容,但许多实际场景需要动态、交互式和数据驱动的内容。这就是动态区块发挥作用的地方,它提供了服务器端 PHP 逻辑与客户端 JavaScript 交互性之间的强大桥梁。

理解动态区块的力量

Gutenberg 中的默认静态区块在保存时会在客户端生成 HTML。然后,此 HTML 直接存储在帖子内容中。虽然对于静态内容来说效率很高,但当处理频繁更改或需要服务器端处理的数据时,这种方法存在局限性。另一方面,动态区块通过使用 PHP 在服务器端渲染其输出来解决此问题。当用户查看包含动态区块的页面时,WordPress 会执行一个 PHP 函数(render_callback)来动态生成区块的 HTML。这使得区块能够获取数据库的实时数据、与其他 WordPress 功能进行交互,并呈现始终最新的信息。

这种服务器端渲染至关重要,原因如下:

  • 实时数据: 显示最新的帖子、即将举行的活动、股票价格或任何其他频繁变化的数据。
  • 复杂逻辑: 在渲染输出之前执行计算、查询自定义帖子类型或与外部 API 集成。
  • 安全性: 在显示数据之前对其进行清理和验证,尤其是在处理用户生成的内容或外部来源时。
  • 性能: 将复杂的渲染任务卸载到服务器有时可以提高客户端性能。

动态区块的结构

动态区块与任何其他 Gutenberg 区块一样,由其元数据和 JavaScript 注册定义。但是,其独特的特征在于注册期间指定的 render_callback 属性。此回调函数负责在服务器上生成区块的 HTML 输出。

以下是关键组件的细分:

  1. 区块注册 (JavaScript): 您仍然需要使用 JavaScript 文件中的 registerBlockType() 来注册您的区块。这会告诉 Gutenberg 您的区块存在以及如何处理其属性和编辑器界面。
  2. 属性: 这些是与您的区块相关联的数据字段,存储在帖子内容中。对于动态区块,属性主要用于在编辑器中配置区块的行为和设置。实际显示的内容由 render_callback 生成。
  3. render_callback (PHP): 这是动态区块的核心。它是一个 PHP 函数,接收区块的属性作为参数,并返回要在前端渲染的 HTML 字符串。
  4. 编辑器界面 (JavaScript): 您仍然可以使用 React 组件在 Gutenberg 编辑器中构建区块的界面。此界面允许用户配置区块的属性并查看动态内容外观的(通常是简化的)预览。

分步:创建动态“最新帖子”区块

让我们创建一个实际示例:一个显示您 WordPress 网站最新帖子列表的动态区块。

先决条件:

  • 本地 WordPress 开发环境。
  • PHP 和 JavaScript 的基本理解。
  • 您将添加区块代码的插件或主题。

步骤 1:设置您的区块插件/主题

如果您没有自定义插件或主题,请创建一个简单的。在此示例中,我们将假设您将其添加到插件中。

步骤 2:注册区块 (JavaScript)

在您的插件的 JavaScript 文件(例如 src/index.js)中,您将注册该区块。我们将使用 @wordpress/blocks@wordpress/i18n 进行国际化。

// src/index.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';

// 导入您的区块的编辑组件
import Edit from './edit';

registerBlockType( 'my-dynamic-blocks/recent-posts', {
    title: __( 'Recent Posts', 'my-dynamic-blocks' ),
    icon: 'list-view',
    category: 'widgets',
    attributes: {
        numberOfPosts: {
            type: 'number',
            default: 5,
        },
        showExcerpt: {
            type: 'boolean',
            default: true,
        },
    },
    edit: Edit,
    // save() 函数对于动态区块不是必需的
    // 服务器端 render_callback 将处理输出
} );

步骤 3:创建编辑器组件 (JavaScript)

现在,创建 src/edit.js 文件。此组件将定义区块在 Gutenberg 编辑器中的外观和行为。

// src/edit.js

import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import ServerSideRender from '@wordpress/server-side-render';

export default function Edit( { attributes, setAttributes } ) {
    const blockProps = useBlockProps();
    const { numberOfPosts, showExcerpt } = attributes;

    return (
        <div { ...blockProps }>
            <InspectorControls>
                <PanelBody title={ __( 'Post Settings', 'my-dynamic-blocks' ) }>
                    <RangeControl
                        label={ __( 'Number of Posts', 'my-dynamic-blocks' ) }
                        value={ numberOfPosts }
                        onChange={ ( value ) => setAttributes( { numberOfPosts: value } ) }
                        min={ 1 }
                        max={ 10 }
                    />
                    <ToggleControl
                        label={ __( 'Show Excerpt', 'my-dynamic-blocks' ) }
                        checked={ showExcerpt }
                        onChange={ ( value ) => setAttributes( { showExcerpt: value } ) }
                    />
                </PanelBody>
            </InspectorControls>
            {
                // 使用 ServerSideRender 来显示动态区块的预览
                <ServerSideRender
                    block="my-dynamic-blocks/recent-posts"
                    attributes={ attributes }
                />
            }
        </div>
    );
}

请注意,我们没有定义 save() 函数。这是动态区块的关键区别。ServerSideRender 组件用于通过向服务器发出请求来直接在编辑器中显示区块输出的预览。

步骤 4:排队脚本并注册服务器端渲染 (PHP)

在您的插件的主 PHP 文件(例如 my-dynamic-blocks.php)中,您需要排队您的 JavaScript 文件并注册区块类型,包括 render_callback

<?php
/**
 * Plugin Name: My Dynamic Blocks
 * Description: A simple plugin for dynamic Gutenberg blocks.
 * Version: 1.0
 * Author: Your Name
 */

function my_dynamic_blocks_register_block() {
    // Register the block type
    register_block_type( 'my-dynamic-blocks/recent-posts', array(
        'editor_script' => 'my-dynamic-blocks-editor-script',
        'render_callback' => 'my_dynamic_blocks_render_recent_posts',
        'attributes' => array(
            'numberOfPosts' => array(
                'type' => 'number',
                'default' => 5,
            ),
            'showExcerpt' => array(
                'type' => 'boolean',
                'default' => true,
            ),
        ),
    ) );
}
add_action( 'init', 'my_dynamic_blocks_register_block' );

function my_dynamic_blocks_enqueue_editor_scripts() {
    wp_enqueue_script(
        'my-dynamic-blocks-editor-script',
        plugins_url( 'build/index.js', __FILE__ ), // 假设您有一个构建过程
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n', 'wp-server-side-render' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
    );
}
add_action( 'enqueue_block_editor_assets', 'my_dynamic_blocks_enqueue_editor_scripts' );

/**
 * Render callback for the Recent Posts dynamic block.
 *
 * @param array $attributes Block attributes.
 * @return string HTML output.
 */
function my_dynamic_blocks_render_recent_posts( $attributes ) {
    $number_of_posts = isset( $attributes['numberOfPosts'] ) ? (int) $attributes['numberOfPosts'] : 5;
    $show_excerpt    = isset( $attributes['showExcerpt'] ) ? (bool) $attributes['showExcerpt'] : true;

    $args = array(
        'posts_per_page' => $number_of_posts,
        'post_status'    => 'publish',
        'orderby'        => 'date',
        'order'          => 'DESC',
        'ignore_sticky_posts' => true,
    );

    $recent_posts_query = new WP_Query( $args );

    $output = '';

    if ( $recent_posts_query->have_posts() ) {
        $output .= '<ul class="wp-block-my-dynamic-blocks-recent-posts">'; // 使用类进行样式设置
        while ( $recent_posts_query->have_posts() ) {
            $recent_posts_query->the_post();
            $output .= '<li>';
            $output .= '<a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a>';
            if ( $show_excerpt ) {
                $output .= '<p>' . get_the_excerpt() . '</p>';
            }
            $output .= '</li>';
        }
        $output .= '</ul>';
        wp_reset_postdata(); // 重要:自定义循环后重置帖子数据
    } else {
        $output = '<p>' . __( 'No posts found.', 'my-dynamic-blocks' ) . '</p>';
    }

    return $output;
}

PHP 代码说明:

  • my_dynamic_blocks_register_block():此函数挂钩到 init 以注册我们的区块类型。关键是,它将 render_callback 定义为 my_dynamic_blocks_render_recent_posts,并传递区块的属性,与 JavaScript 中定义的相呼应。
  • my_dynamic_blocks_enqueue_editor_scripts():此函数挂钩到 enqueue_block_editor_assets 以加载我们专门用于编辑器的 JavaScript 文件。它包含 wp-server-side-render 作为依赖项。
  • my_dynamic_blocks_render_recent_posts():这是我们的 render_callback。它接收 $attributes 数组,对其进行清理和类型转换,然后使用 WP_Query 获取最新帖子。它构建一个 HTML <ul> 列表并返回它。wp_reset_postdata() 在自定义 WP_Query 循环后对于恢复原始查询和帖子数据至关重要。

步骤 5:构建您的 JavaScript

如果您使用现代 JavaScript 构建过程(如 @wordpress/scripts),您将需要构建您的资源。在插件目录中运行 npm run build(或 yarn build)。

步骤 6:激活和测试

激活您的插件。现在,当您将“最新帖子”区块添加到帖子或页面时,您将看到编辑器界面。预览区域将显示由您的 PHP 回调渲染的输出。当您发布或更新帖子时,相同的 PHP 回调将在前端运行以显示动态内容。

最佳实践和注意事项

  • 安全第一:render_callback 中使用来自属性或外部来源的任何数据之前,请务必对其进行清理和验证。使用 esc_html()esc_url()sanitize_text_field() 等 WordPress 函数。
  • wp_reset_postdata() 如果您的 render_callback 使用 WP_Query 或修改全局 $post 对象,请务必之后调用 wp_reset_postdata() 以恢复原始查询和帖子数据。
  • 性能考虑: 虽然动态区块功能强大,但过多的或复杂的服务器端渲染可能会影响页面加载时间。优化您的查询和渲染逻辑。如果性能成为问题,请考虑缓存机制。
  • 编辑器预览: ServerSideRender 组件提供了良好的预览,但它依赖于发出服务器请求。对于非常复杂的区块,这可能不是即时的。确保您的 render_callback 效率高。
  • 属性处理: 属性主要用于在编辑器中配置区块。render_callback 负责获取和显示实际的动态内容。避免将大量动态数据直接存储在属性中。
  • 命名约定: 为您的区块名称(例如 my-plugin-slug/block-name)和 PHP 函数名称使用唯一的名称前缀,以避免与其他插件或主题发生冲突。
  • 国际化: 在 JavaScript 和 PHP 代码中使用 __()_n() 进行可翻译字符串。

超越最新帖子:扩展动态区块功能

“最新帖子”示例只是冰山一角。您可以利用动态区块来实现:

  • 显示自定义帖子类型: 展示产品、作品集项目或活动。
  • 用户特定内容: 显示与登录用户相关的内容。
  • 交互式表单: 虽然复杂的表单可能仍然受益于专用插件,但可以构建简单的动态表单。
  • 数据可视化: 从 API 获取数据并渲染图表或图形(但这通常涉及更高级的 JavaScript 库)。
  • 与其他插件集成: 动态显示由其他插件管理的数据。

结论

动态 WordPress 区块是开发人员在 Gutenberg 编辑器中创建真正交互式和数据驱动的体验的必备工具。通过了解如何使用 render_callback 函数注册区块并有效管理属性,您可以构建获取和显示实时信息的强大组件。虽然静态区块在结构化内容方面发挥着作用,但动态区块解锁了新的功能级别,使您能够弥合服务器端逻辑与前端表示之间的差距。通过遵循安全、性能和代码组织的最佳实践,您可以自信地开发健壮且有用的动态区块,以增强您的 WordPress 网站。

Sources (5)