WP DEVELOP

EP221. “迁移最后两个 Block:无 render.php 场景与模板 class 清理”

首页 WordPress 开发课程 现代化 BLOCK 开发标准做法 · EP221
约 20 分钟· #EP221#现代化 BLOCK 开发标准做法
🔒 登录后可标记已读

迁移这一章最后两个 Block——genericheading(标题)和 genericbutton(按钮)。这两个 Block 有一个共同的特殊之处:它们从建立之初就没有用 PHP 渲染回调save 函数直接输出静态 HTML 存进数据库,不需要任何服务器端动态决策——所以 block.json 里完全不需要 render 属性。这一讲也集中处理了一个「用了 apiVersion: 3 之后必然会遇到」的收尾问题:新增的选中态外壳 <div {...blockProps}> 会让编辑器生成的 HTML 多出一层 class(比如 wp-block-ourblocktheme-genericheading),跟模板文件(front-page.html)里写死的旧版 HTML 不匹配,需要手动去模板文件里把这些多余的 class 清理掉,才能让「区块验证」通过。做完这一讲,整个 Block Theme 的全部 Block 都已经迁移到现代化标准做法,旧的 our-blocks/ 文件夹可以彻底删除。


涉及文件

  • wp-content/themes/fictional-clean-blocks/functions.php (修改,删除整个旧的 JSXBlock PHP 类,genericheading/genericbutton 切换成 register_block_type_from_metadata
  • wp-content/themes/fictional-clean-blocks/src/genericheading/ 整套(新建)
  • wp-content/themes/fictional-clean-blocks/src/genericbutton/ 整套(新建,参照 genericheading/ 复制再改)
  • wp-content/themes/fictional-clean-blocks/templates/front-page.html (修改,清理多余 class)
  • 删除整个旧的 our-blocks/ 文件夹

代码实现

src/genericheading/block.json(新建,没有 render 属性)

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "ourblocktheme/genericheading",
  "title": "Fictional University Generic Heading",
  "attributes": {
    "text": { "type": "string" },
    "size": { "type": "string", "default": "large" }
  },
  "editorScript": "file:./index.js"
}

src/genericheading/edit.js(完整文件,从旧的 our-blocks/genericheading.js 搬运)

import { ToolbarGroup, ToolbarButton } from "@wordpress/components"
import { RichText, BlockControls, useBlockProps } from "@wordpress/block-editor"

export default function Edit(props) {
  const blockProps = useBlockProps()

  function handleTextChange(x) {
    props.setAttributes({ text: x })
  }

  return (
    <div {...blockProps}>
      <BlockControls>
        <ToolbarGroup>
          <ToolbarButton
            isPressed={props.attributes.size === "large"}
            onClick={() => props.setAttributes({ size: "large" })}
          >
            Large
          </ToolbarButton>
          <ToolbarButton
            isPressed={props.attributes.size === "medium"}
            onClick={() => props.setAttributes({ size: "medium" })}
          >
            Medium
          </ToolbarButton>
          <ToolbarButton
            isPressed={props.attributes.size === "small"}
            onClick={() => props.setAttributes({ size: "small" })}
          >
            Small
          </ToolbarButton>
        </ToolbarGroup>
      </BlockControls>
      <RichText
        allowedFormats={["core/bold", "core/italic"]}
        tagName="h1"
        className={`headline headline--${props.attributes.size}`}
        value={props.attributes.text}
        onChange={handleTextChange}
      />
    </div>
  )
}

src/genericheading/index.js(完整文件,save 函数直接内联在这里,因为它本来就来自「有 render.php 的 Block 才需要拆分」这条判断标准之外——这里是纯 JS 输出,本来就写在 index.js

import { RichText } from "@wordpress/block-editor"
import { registerBlockType } from "@wordpress/blocks"
import metadata from "./block.json"
import Edit from "./edit"

function Save(props) {
  function createTagName() {
    switch (props.attributes.size) {
      case "large":
        return "h1"
      case "medium":
        return "h2"
      case "small":
        return "h3"
    }
  }

  return (
    <RichText.Content
      tagName={createTagName()}
      value={props.attributes.text}
      className={`headline headline--${props.attributes.size}`}
    />
  )
}

registerBlockType(metadata.name, {
  edit: Edit,
  save: Save
})

genericbutton:整体结构跟 genericheading 一致,block.json 换成对应的 text/size/linkObject/colorName 属性,edit.js/index.js 分别搬运旧版 genericbutton.js 里的 EditComponent/SaveComponent(内容跟 Block Theme 章节 EP189-191 建立的逻辑一致,此处不重复贴)

functions.php:整个删除旧的 JSXBlock 类,用 register_block_type_from_metadata() 收尾

register_block_type_from_metadata(__DIR__ . '/build/genericheading');
register_block_type_from_metadata(__DIR__ . '/build/genericbutton');

templates/front-page.html:清理编辑器新生成的多余 class

<!-- 旧版(迁移前):编辑器/浏览器自动生成的 wp-block-{namespace}-{block名} class -->
<h1 class="wp-block-ourblocktheme-genericheading headline headline--large">Welcome!</h1>

<!-- 新版(清理后,跟模板文件里手写的原始版本一致) -->
<h1 class="headline headline--large">Welcome!</h1>

关键改动点:

  • genericheading/genericbutton 是这套 Block Theme 里唯二「不需要 PHP 渲染回调」的 Block:从最初设计时就决定让它们只输出静态 HTML(save 函数算出最终结果、直接存进数据库),不需要任何服务器端动态查询/判断——这类 Block 的 block.json 里完全不写 render 属性,少一个步骤
  • 复制近似 Block 时要小心「复制源本身可能已经不是最简单的了」:这一讲第一次尝试复制一个「简单占位 Block」(比如 archive)当起点,因为这类占位 Block render.php 是空的、跟 genericheading 不需要 render.php 的情况更接近;genericbutton 则直接复制刚做完的 genericheading 文件夹再改
  • 踩到的真实 bug:把两个 Block 的 attributes 写反了——因为两个文件夹是前后脚复制出来的,内容高度相似,一不小心把 genericheadingblock.json 填成了 genericbutton 的属性,反之亦然;这类错误没有明显的语法报错,只有在编辑器实际使用时才会暴露(属性对不上导致工具栏/富文本行为跟预期不符),提醒复制文件迁移时要格外确认「改的是不是正确的那一份」
  • apiVersion: 3 强制加的 <div {...blockProps}> 外壳,会让最终 HTML 多一层 wp-block-{命名空间}-{Block名} 这个 class(比如 wp-block-ourblocktheme-genericheading)——这是 WordPress 自动生成、用来辅助管理 Block 的通用 class;但模板文件(front-page.html)里手写的旧版 HTML 没有这个 class,两者对不上会触发「区块验证失败」的报错(浏览器控制台能看到具体的「预期 HTML vs 实际 HTML」对比)
  • 修复方式:手动去模板文件里把这个多余 class 删掉,让模板文件里写死的 HTML 跟新版 Block 输出的结构重新对齐——这一步不是自动的,需要过一遍模板文件里所有用到 genericheading/genericbutton 的地方,逐个清理
  • 别忘了先清除编辑器里保存过的旧自定义版本:跟这一章反复遇到的情况一样,改完模板文件源码后,如果数据库里还存着旧的自定义版本,页面依然会显示旧内容——要先在全站编辑器里「清除自定义项」,确保用的是刚改好的模板文件版本
  • 两个 Block 全部迁移完成后,functions.php 里那个曾经承载了整章「手工方案」核心逻辑的 JSXBlock PHP 类,可以整个删除——这是这一章从头到尾迁移工作的收官标志:曾经复杂的自定义类被 register_block_type_from_metadata() 一行代码彻底取代
  • 旧的 our-blocks/ 文件夹此时可以整个删除:所有曾经存在这里的 .js/.php 文件此时都已经有了对应的 src/{block名}/ 现代化版本,不再需要保留任何旧代码

Hook / Function 速查

名称类型用途
block.json 不写 render 属性配置约定适用于纯 JS/静态 HTML 输出、不需要服务器端动态渲染的 Block
wp-block-{命名空间}-{Block名}(自动生成的 class)useBlockProps() 附带效果apiVersion: 3 下 WordPress 自动追加的辅助管理 class,可能导致模板文件里手写的旧 HTML 出现验证不匹配

常见坑

  • 给不需要动态渲染的 Block 也画蛇添足地保留一个空的 render.php 并在 block.json 里声明 render 属性——虽然不一定报错,但徒增文件、偏离了「纯静态输出不需要 PHP 渲染」这条设计初衷
  • 前后脚复制多个相似文件夹时,把不同 Block 各自的 attributes 内容改混、张冠李戴——没有语法错误提示,只有在实际使用时才会发现工具栏/富文本行为不对
  • 升级到 apiVersion: 3 后忘记同步清理模板文件里手写的旧 HTML(缺少 wp-block-* 这个自动生成的 class)——会导致编辑器提示「区块验证失败」
  • 清理完模板文件的 class 之后,忘记先在编辑器里清除已保存的自定义版本——页面继续显示数据库里的旧内容,误以为改动没生效

[截图:模板文件手写 HTML 跟新版 Block 输出的 wp-block-* class 对不上时,编辑器弹出的"区块已被修改/验证失败"提示,含浏览器控制台里预期 HTML 与实际 HTML 的对比]


延伸 / 后续讲座会用到

下一讲要清理构建产物里因为反复复制文件夹产生的多余文件夹(比如 archive copy),并把 package.json 的构建脚本整理成不需要手动罗列所有 Block 入口的简洁版本。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 29, EP221