WORDPRESS DEVELOPER GUIDE

古腾堡编辑器
区块开发完全指南

掌握为现有区块添加自定义样式与交互功能,以及创建安全的iframe嵌入区块的专业技术

核心要点

  • 使用 register_block_style 扩展段落样式
  • 通过 enqueue 添加交互脚本
  • 创建自定义 iframe 嵌入区块
  • 实现 XSS 安全防护

核心API函数

8+

安全考量点

XSS防护

开发模式

动态渲染

引言

为 WordPress 古腾堡编辑器的现有段落区块添加额外样式与交互功能,可以通过 register_block_style 函数注册自定义样式,并结合 enqueue 脚本加载 JavaScript 实现交互。

创建自定义的 iframe 嵌入区块则需使用 registerBlockType 定义新区块,在 edit.js 中利用 TextControlCodeEditor 获取用户 HTML 输入,并在 save.js 中通过 iframe 的 srcdoc 属性嵌入用户 HTML,同时务必使用 wp_kses 等函数对用户输入进行净化以防范 XSS 风险。

样式扩展

通过 register_block_style 函数,开发者可以为核心区块(如段落区块)添加自定义的视觉样式变体,无需修改区块核心代码。

  • • 标准化样式注册流程
  • • 自动添加 CSS 类名
  • • 支持内联样式或外部样式表

交互增强

结合 wp_enqueue_script 机制,可以为带有特定样式的段落添加交互功能,实现点击事件、悬停效果等复杂行为。

  • • 基于类名的元素选择
  • • 事件绑定与处理
  • • 编辑器与前端一致性

1. 为现有段落区块添加额外样式与交互功能

1.1 使用 register_block_style 添加自定义样式

在 WordPress 古腾堡编辑器中,开发者可以通过 register_block_style 函数为核心区块(如段落区块 core/paragraph)添加自定义的视觉样式变体。这个功能允许用户在不修改区块核心代码的前提下,为内容呈现提供多样化的外观选择。register_block_style 函数通常在主题的 functions.php 文件或自定义插件中调用,并通过 WordPress 的 init 钩子进行注册,确保在 WordPress 初始化时样式变体可用。

// 注册段落区块的自定义样式
add_action( 'init', 'register_custom_block_styles' );

function register_custom_block_styles() {
  register_block_style( 'core/paragraph', [
    'name' => 'colored-bottom-border',
    'label' => __( 'Colored Bottom Border', 'text-domain' ),
    'inline_style' => '.is-style-colored-bottom-border { border-bottom: 3px solid #3b82f6; padding-bottom: 0.5rem; }'
  ] );
}

关键参数说明

  • name: 样式变体的唯一标识符
  • label: 在编辑器中显示给用户的样式名称
  • inline_style: 直接内联的 CSS 规则
  • style_handle: 已注册的样式表句柄

当用户在前端选择并应用某个自定义样式时,WordPress 会自动向该区块的包装元素添加一个特定的 CSS 类,格式通常为 is-style-{name},其中 {name} 是在注册时定义的样式名称。例如,如果注册了一个名为 'colored-bottom-border' 的样式,应用该样式的段落区块会获得 is-style-colored-bottom-border 类。

1.2 通过 enqueue 脚本为自定义样式添加 JavaScript 交互

在 WordPress 古腾堡编辑器开发中,为现有区块(如 core/paragraph)添加自定义样式后,若需进一步为这些带有特定样式的段落添加交互功能,一种可行的方案是通过 wp_enqueue_script 函数在页面中加载自定义的 JavaScript 文件。

// 加载交互脚本
add_action( 'wp_enqueue_scripts', 'enqueue_interactive_scripts' );

function enqueue_interactive_scripts() {
  wp_enqueue_script(
    'custom-interactive-script',
    get_template_directory_uri() . '/js/interactive.js',
    array(),
    filemtime( get_template_directory() . '/js/interactive.js' ),
    true
  );
}
// interactive.js - 为自定义样式添加交互功能
document.addEventListener( 'DOMContentLoaded', function() {
  const customParagraphs = document.querySelectorAll( '.is-style-custom-interactive-style' );

  customParagraphs.forEach( paragraph => {
    paragraph.addEventListener( 'click', function() {
      this.classList.toggle( 'expanded' );
      this.style.backgroundColor = this.classList.contains( 'expanded' )
        ? '#f0f9ff'
        : 'transparent';
    } );
  } );
} );

注意事项

当编辑器或主题更新时,这种依赖于特定类名的 JavaScript 选择器可能会因为 DOM 结构的变化而失效,因此需要仔细测试和维护。

2. 创建自定义 iframe 嵌入区块

2.1 使用 registerBlockType 创建新区块

在 WordPress 古腾堡编辑器中创建自定义区块,核心步骤是使用 registerBlockType 函数。这个函数负责向编辑器注册一个新的区块类型,使其能够被用户插入和使用。根据搜索结果中的信息,registerBlockType 函数接受一个块名称(通常是插件或主题的命名空间加上块名称)和一个配置对象作为参数。

基本配置参数

  • title: 区块显示名称
  • category: 区块分类(common、formatting、layout等)
  • icon: Dashicon 图标或自定义 SVG
  • attributes: 定义区块的可编辑属性
  • edit: 编辑器渲染函数
  • save: 保存与前端渲染函数

属性类型支持

  • string: 字符串类型
  • number: 数字类型
  • boolean: 布尔值类型
  • array: 数组类型
  • object: 对象类型
// 注册自定义 iframe 嵌入区块
registerBlockType( 'my-plugin/custom-iframe', {
  title: __( 'Custom Iframe', 'my-plugin' ),
  category: 'embed',
  icon: 'embed-generic',
  attributes: {
    htmlContent: {
      type: 'string',
      source: 'html',
      selector: 'iframe',
    },
    width: {
      type: 'string',
      default: '100%',
    },
    height: {
      type: 'string',
      default: '400px',
    }
  },
  edit: EditComponent,
  save: SaveComponent
} );

2.2 在 edit.js 中使用 TextControl 或 CodeEditor 获取用户 HTML 输入

在自定义区块的 edit.js 文件中,开发者需要使用合适的 WordPress 组件来获取用户的输入。对于需要用户输入 HTML 源码的场景,例如创建一个 iframe 嵌入区块,可以考虑使用 TextControl 或 CodeEditor 组件Amilia Store 插件的案例中,使用了 TextControl 组件来配置 iframe 的 URL。

// edit.js - 编辑组件实现
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { TextControl, TextareaControl } from '@wordpress/components';

const EditComponent = ( props ) => {
  const { attributes, setAttributes } = props;
  const { htmlContent, width, height } = attributes;

  return (
    <div { ...useBlockProps() }>
      <TextControl
        label={ __( 'Width', 'my-plugin' ) }
        value={ width }
        onChange={ ( newWidth ) => setAttributes( { width: newWidth } ) }
      />
      <TextControl
        label={ __( 'Height', 'my-plugin' ) }
        value={ height }
        onChange={ ( newHeight ) => setAttributes( { height: newHeight } ) }
      />
      <TextareaControl
        label={ __( 'HTML Content', 'my-plugin' ) }
        value={ htmlContent }
        onChange={ ( newContent ) => setAttributes( { htmlContent: newContent } ) }
        help={ __( 'Enter the HTML code to embed in the iframe', 'my-plugin' ) }
      />
    </div>
  );
};

数据流机制

通过 props.attributes 可以访问到区块定义的属性,并通过 props.setAttributes 函数来更新这些属性。这种数据流确保了用户在编辑器中修改区块内容时,相应的属性值会被更新,并最终传递给 save 函数进行处理。

2.3 在 save.js 中使用 iframe 的 srcdoc 属性嵌入用户 HTML

在自定义区块的 save.js 文件中,save 函数负责定义区块内容如何被序列化并保存到数据库,以及如何在网站前端呈现。对于需要嵌入用户自定义 HTML 的场景,例如创建一个 iframe 区块,可以使用 HTML 的 iframe 元素,并结合其 srcdoc 属性。srcdoc 属性允许直接将 HTML 代码作为字符串嵌入到 iframe 中,而不是通过 src 属性引用一个外部 URL。

// save.js - 保存组件实现
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';

const SaveComponent = ( props ) => {
  const { attributes } = props;
  const { htmlContent, width, height } = attributes;

  return (
    <div { ...useBlockProps.save() }>
      <iframe
        width={ width }
        height={ height }
        srcdoc={ htmlContent }
        frameBorder="0"
        title={ __( 'Embedded Content', 'my-plugin' ) }
      />
    </div>
  );
};

安全警告

直接输出用户提供的 HTML 存在安全风险,因此在将其插入 srcdoc 之前,必须进行严格的净化处理,以防止跨站脚本攻击 (XSS)。这一点将在后续的安全考虑部分详细讨论。

2.4 安全考虑:使用 wp_kses 等函数净化用户输入的 HTML

在 WordPress 开发中,处理用户输入时,安全性是至关重要的,尤其是在允许用户输入 HTML 并将其嵌入到页面中的场景。跨站脚本攻击 (XSS) 是一种常见的安全漏洞,攻击者通过在网页中注入恶意脚本,从而在用户浏览网页时执行这些脚本,可能导致用户会话被盗、网站内容被篡改等严重后果。

// PHP 端 HTML 净化处理
add_filter( 'render_block_my-plugin/custom-iframe', 'sanitize_iframe_content', 10, 2 );

function sanitize_iframe_content( $block_content, $block ) {
  if ( ! empty( $block['attrs']['htmlContent'] ) ) {
    // 使用 wp_kses_post 进行净化
    $sanitized_content = wp_kses_post( $block['attrs']['htmlContent'] );

    // 或者使用自定义的允许标签列表
    $allowed_html = array(
      'p' => array(),
      'strong' => array(),
      'em' => array(),
      'a' => array( 'href', 'title', 'target' ),
      'img' => array( 'src', 'alt', 'width', 'height' )
    );
    $sanitized_content = wp_kses( $block['attrs']['htmlContent'], $allowed_html );

    // 更新区块内容
    $block['attrs']['htmlContent'] = $sanitized_content;
  }

  return $block_content;
}

为了防止 XSS 攻击,WordPress 提供了一系列的数据净化 (Sanitization) 和转义 (Escaping) 函数wp_kses() 函数是其中一个非常重要的函数,它可以根据指定的允许 HTML 标签和属性列表,过滤掉用户输入中所有不被允许的内容。例如,wp_kses_post() 是一个常用的包装函数,它使用 wp_kses() 并允许在文章内容中通常允许的 HTML 标签和属性。

3. 安全性与最佳实践

3.1 处理用户输入 HTML 的安全风险 (XSS)

允许用户输入 HTML 并将其嵌入到网页中,为跨站脚本攻击 (XSS) 提供了可乘之机。XSS 攻击的原理是攻击者通过在用户输入中注入恶意脚本(通常是 JavaScript 代码),当这些内容被浏览器解析和执行时,就可能窃取用户 cookie、会话令牌,修改网页内容,或者将用户重定向到恶意网站。

示例攻击: 如果用户在区块中输入 <script>alert('XSS');</script>,并且这段代码被直接保存并输出到前端页面,那么任何访问该页面的用户都会执行这段脚本。

3.2 使用 wp_kses_post 或 wp_kses 进行输入净化

为了防范 XSS 攻击,WordPress 提供了强大的数据净化函数 wp_kses()。这个函数通过一个允许的 HTML 标签和属性数组(白名单)来过滤输入内容,移除所有不在白名单上的标签和属性。

  • wp_kses_post(): 使用文章内容默认允许的 HTML 标签
  • wp_kses(): 自定义允许的标签和属性列表
  • 白名单机制: 只允许安全的 HTML 标记通过

3.3 避免直接使用 dangerouslySetInnerHTML 的风险

在 React 应用中,dangerouslySetInnerHTML 是一个特殊的属性,它允许开发者直接将 HTML 字符串插入到 DOM 元素中。正如其名所示,这个属性是"危险"的,因为它绕过了 React 默认的转义机制,使得应用容易受到 XSS 攻击。

// 危险的用法 - 避免直接使用
const DangerousComponent = ( { html } ) => {
  return <div dangerouslySetInnerHTML={ { __html: html } } />;
};

// 安全的使用方式 - 先净化再使用
const SafeComponent = ( { html } ) => {
  // 使用净化库处理 HTML
  const cleanHtml = sanitizeHtmlLibrary( html );
  return <div dangerouslySetInnerHTML={ { __html: cleanHtml } } />;
};

最佳实践建议

GitHub issue 明确指出,虽然 save 函数需要使用 dangerouslySetInnerHTML 属性来包含原始 HTML,但开发者必须确保在使用前进行严格的净化处理。最佳实践是尽可能避免使用 dangerouslySetInnerHTML,尤其是在处理用户输入时。

3.4 考虑使用官方 Embed 区块或现有插件作为替代方案

在 WordPress 古腾堡编辑器中嵌入外部内容或 iframe 时,开发者并不总是需要从头开始创建自定义区块。WordPress 核心已经提供了一个功能强大的 core/embed 区块,它支持通过 oEmbed 协议嵌入来自众多流行平台(如 YouTube, Vimeo, Twitter, Instagram 等)的内容。oEmbed 是一种标准,允许第三方网站通过 URL 提供内容的嵌入代码,WordPress 能够自动处理这些 URL 并将其转换为相应的嵌入内容。

内置 Embed 区块

  • • YouTube, Vimeo 视频
  • • Twitter, Facebook 社交
  • • Instagram, Flickr 图片
  • • Spotify, SoundCloud 音频

推荐插件

评估标准

  • • 安全性考虑
  • • 功能需求匹配
  • • 维护成本
  • • 用户体验

4. 核心区块扩展与自定义区块开发进阶

4.1 修改核心区块的 supports 属性

WordPress 区块的 supports 属性在 block.json 文件中定义,它声明了区块支持哪些内置功能和特性,例如对齐方式 (align)、颜色设置 (color)、排版 (typography)、间距 (spacing) 等。开发者可以通过 WordPress 提供的 API 来修改这些默认的 supports 设置。

// 修改核心段落区块的 supports 属性
add_filter( 'register_block_type_args', 'modify_core_paragraph_supports', 10, 2 );

function modify_core_paragraph_supports( $args, $name ) {
  if ( 'core/paragraph' === $name ) {
    $args['supports']['align'] = array( 'left', 'center', 'right', 'wide', 'full' );
    // $args['supports']['color']['background'] = false;
  }
  return $args;
}

4.2 使用 filter 钩子修改区块设置

WordPress 提供了强大的过滤器 (filter) 钩子机制,允许开发者修改核心区块的设置或向区块编辑器中注入自定义控件。blocks.registerBlockType 过滤器可以用于修改任何区块(包括核心区块)的注册设置。

// 使用 HOC 添加自定义控件
const withCustomControl = createHigherOrderComponent( ( BlockEdit ) => {
  return ( props ) => {
    if ( props.name !== 'core/paragraph' ) {
      return <BlockEdit { ...props } />;
    }

    return (
      <>
        <BlockEdit { ...props } />
        <InspectorControls>
          <ToggleControl
            label="Mobile Visibility"
            checked={ props.attributes.visibleOnMobile }
            onChange={ ( value ) => props.setAttributes( { visibleOnMobile: value } ) }
          />
        </InspectorControls>
      </>
    );
  };
}, 'withCustomControl' );

4.3 创建动态区块 (Dynamic Blocks) 与服务器端渲染

动态区块 (Dynamic Blocks) 是一种特殊类型的 Gutenberg 区块,其内容不是在编辑时静态保存到 post_content 中,而是在每次页面加载时通过服务器端 PHP 代码动态渲染。这对于需要显示实时数据、复杂查询结果或内容依赖于外部因素的区块非常有用。

动态区块特点

  • • 服务器端渲染内容
  • • 实时数据展示
  • • 复杂查询结果
  • • 外部依赖内容

实现方式

  • • save 函数返回 null 或占位符
  • • PHP 端注册渲染回调
  • • 使用 ServerSideRender 组件
  • • 编辑器预览支持
// 动态区块的 save 函数
registerBlockType( 'my-plugin/dynamic-block', {
  // ...其他配置
  save: () => null, // 或返回简单占位符
} );

// PHP 端注册渲染回调
register_block_type( 'my-plugin/dynamic-block', array(
  'render_callback' => 'render_dynamic_block',
) );

function render_dynamic_block( $attributes, $content ) {
  // 动态查询数据
  $latest_posts = get_posts( array(
    'numberposts' => 3,
    'post_status' => 'publish',
  ) );

  // 构建 HTML 输出
  ob_start();
  ?>
  <div class="latest-posts-block">
    <h3><?php echo esc_html( $attributes['title'] ); ?></h3>
    <ul>
      <?php foreach ( $latest_posts as $post ) : ?>
        <li>
          <a href="<?php echo esc_url( get_permalink( $post ) ); ?>">
            <?php echo esc_html( $post->post_title ); ?>
          </a>
        </li>
      <?php endforeach; ?>
    </ul>
  </div>
  <?php
  return ob_get_clean();
}

编辑器预览支持

在编辑器中,为了预览动态内容,可以使用 ServerSideRender 组件(来自 @wordpress/server-side-render 包)。这个组件会在编辑器内发起一个 AJAX 请求到服务器,执行渲染回调函数,并将返回的 HTML 显示在区块的编辑视图中。

4.4 使用 InnerBlocks 处理嵌套区块内容

InnerBlocks 组件是 Gutenberg 区块开发中一个非常强大的工具,它允许一个父区块内部包含和管理一组子区块。这使得创建复杂的、可嵌套的布局结构成为可能,例如列布局、选项卡、手风琴、可重复的条目列表等。

// 使用 InnerBlocks 创建可嵌套的布局区块
import { InnerBlocks } from '@wordpress/block-editor';

const ALLOWED_BLOCKS = [
  'core/paragraph',
  'core/image',
  'core/heading',
  'core/list'
];

const TEMPLATE = [
  [ 'core/heading', { level: 2, placeholder: 'Section Title' } ],
  [ 'core/paragraph', { placeholder: 'Add description...' } ]
];

const EditComponent = ( props ) => {
  return (
    <div { ...useBlockProps() }>
      <InnerBlocks
        allowedBlocks={ ALLOWED_BLOCKS }
        template={ TEMPLATE }
        templateLock="insert"
        orientation="vertical"
      />
    </div>
  );
};

const SaveComponent = ( props ) => {
  return (
    <div { ...useBlockProps.save() }>
      <InnerBlocks.Content />
    </div>
  );
};

核心功能

  • • 嵌套区块管理
  • • 模板预设
  • • 允许区块限制
  • • 方向控制

应用场景

  • • 列布局系统
  • • 选项卡组件
  • • 手风琴菜单
  • • 卡片容器

优势

  • • 提高复用性
  • • 增强灵活性
  • • 简化复杂布局
  • • 统一管理

4.5 区块样式变体的注册与管理

区块样式变体(Block Style Variations)是 WordPress 古腾堡编辑器中的一个重要特性,它允许开发者为区块定义一组预设的视觉样式,供内容编辑者在编辑器中选择和应用。根据搜索结果,区块样式变体的核心目标是让内容创作者能够使用预制的样式选项,确保品牌一致性并简化内容生产。

// JavaScript 注册区块样式
wp.domReady( function() {
  wp.blocks.registerBlockStyle( 'core/button', {
    name: 'custom-button-style',
    label: __( 'Custom Button Style', 'text-domain' ),
    isDefault: false
  } );
} );
// PHP 注册区块样式
add_action( 'init', 'register_custom_block_styles' );

function register_custom_block_styles() {
  register_block_style( 'core/button', array(
    'name' => 'custom-button-style',
    'label' => __( 'Custom Button Style', 'text-domain' ),
    'inline_style' => '.is-style-custom-button-style { border-radius: 0; background: #1e40af; }'
  ) );
}

WordPress 6.6+ 增强功能

WordPress 6.6 版本引入了 style_data 参数,允许使用类似 theme.json 的结构来定义样式。此外,还可以通过在主题的 /styles 文件夹下创建 JSON 文件来定义区块样式,这种方式尤其适用于希望样式能够响应全局样式变化的情况。

总结与展望

通过本指南,我们深入探讨了 WordPress 古腾堡编辑器区块开发的核心技术,从扩展现有段落区块的自定义样式与交互功能,到创建安全的自定义 iframe 嵌入区块。我们强调了安全性在区块开发中的重要性,特别是防范 XSS 攻击的关键措施。

核心技术

  • • register_block_style 样式注册
  • • registerBlockType 区块创建
  • • wp_kses HTML 净化
  • • InnerBlocks 嵌套管理

安全实践

  • • XSS 攻击防范
  • • 用户输入净化
  • • 避免危险 API
  • • 使用官方解决方案

进阶技巧

  • • 动态区块渲染
  • • 服务器端处理
  • • 区块样式变体
  • • 过滤器钩子扩展

随着 WordPress 古腾堡编辑器的持续发展,区块开发技术也在不断演进。建议开发者持续关注官方文档更新,参与社区讨论,并及时将安全最佳实践应用到实际项目中。