WP DEVELOP

EP179-180. “主题内自建 Block:复用传统主题 CSS 到编辑器”

首页 WordPress 开发课程 BLOCK THEME(2024 最佳实践) · EP179-180
约 20 分钟· #EP179-180#BLOCK THEME(2024 最佳实践)
🔒 登录后可标记已读

📌 说明:EP179 的 transcript 在「Let's go ahead and create a subfolder. Now, the name doesn't matter. You could make up any [name]」这句话戛然而止,EP180 开头是同一句话的重复接续——两者是同一段实操内容被切成两个文件,合并成一篇笔记。


动手写第一个自定义 Block——首页顶部的 Banner 区块,这一讲的目标只是让它在编辑器里的外观先跟前台一致(静态展示,暂不支持点击编辑文字/换图,这些留到后面几讲)。这一讲要做两个关键决策:① Block 直接写在主题文件夹里(our-blocks/ 子文件夹),而不是像之前「Are You Paying Attention」「Featured Professor」那样独立做成插件——因为这是一个「主题即网站」的客户定制项目,永远不会被换主题,做成插件反而分散精力,如果以后要做成能公开分发的通用主题,再把 Block 拆成独立插件也不迟;② 编辑器要用的 CSS,直接整个引入传统主题已经写好的全局样式表(而不是像独立插件 Block 那样为每个 Block 单独拆分精简的 CSS),因为这是同一个网站不同呈现方式的转换,没必要把已经写好的完整样式重新拆分一遍。


涉及文件

  • wp-content/themes/fictional-block-theme/our-blocks/banner.js (新建)
  • wp-content/themes/fictional-block-theme/package.json (新建,从传统主题复制并修改 start 脚本)
  • wp-content/themes/fictional-block-theme/functions.php (新建,从传统主题复制并新增 Block 注册逻辑)
  • 从传统主题 fictional-university 复制过来(原样保留,不改内容):build/css/images/inc/template-parts/src/ 各文件夹

代码实现

our-blocks/banner.js(新建)——把传统主题 front-page.php 里的 Banner HTML 手动转成 JSX

wp.blocks.registerBlockType("ourblocktheme/banner", {
  title: "Banner",
  edit: EditComponent,
  save: SaveComponent
})

function EditComponent() {
  return (
    <div className="page-banner">
      <div className="page-banner__bg-image" style={{ backgroundImage: "url('/wp-content/themes/fictional-block-theme/images/library-hero.jpg')" }}></div>
      <div className="page-banner__content container t-center c-white">
        <h1 className="headline headline--large">Welcome!</h1>
        <h2 className="headline headline--medium">We think you&rsquo;ll like it here.</h2>
        <h3 className="headline headline--small">
          Why don&rsquo;t you check out the <strong>major</strong> you&rsquo;re interested in?
        </h3>
        <a href="#" className="btn btn--large btn--blue">
          Find Your Major
        </a>
      </div>
    </div>
  )
}

function SaveComponent() {
  return <p>This is from our block.</p>
}

package.jsonstart 脚本改成同时处理两个入口文件

"scripts": {
  "start": "wp-scripts start src/index.js our-blocks/banner.js",
  "test": "echo \"Error: no test specified\" && exit 1"
}

functions.php:注册 Block 用到的 JS 脚本 + Block 类型 + 引入编辑器全局样式

function bannerBlock() {
  wp_register_script('bannerBlockScript', get_stylesheet_directory_uri() . '/build/banner.js', array('wp-blocks', 'wp-editor'));
  register_block_type("ourblocktheme/banner", array(
    'editor_script' => 'bannerBlockScript'
  ));
}
add_action('init', 'bannerBlock');

functions.php:在原有的 university_features() 函数(或同类主题特性注册函数)里追加引入全局 CSS 到编辑器

add_theme_support('editor-styles');
add_editor_style(array(
  'https://fonts.googleapis.com/css?family=Roboto+Condensed:300,300i,400,400i,700,700i|Roboto:100,300,400,400i,700,700i',
  'build/style-index.css',
  'build/index.css'
));

关键改动点:

  • Block 放主题里 vs 做成独立插件——取决于项目性质:如果主题要公开分发/给很多不同客户用,自定义 Block 应该做成独立插件,这样换主题也不会丢功能;但 Fictional University 是「主题即网站」的定制项目,客户 100% 不会换主题,做成插件只会增加不必要的心智负担(在多个插件文件夹和窗口之间来回切换)——这一整章为了让学习者专注理解 Block Theme/FSE 本身的概念,统一选择把 Block 直接放进主题的 our-blocks/ 子文件夹;如果以后真要拆成可分发的插件,这一章学到的概念可以直接套用之前插件章节学过的做法
  • registerBlockType 用到的命名空间是 ourblocktheme(全部小写连写,不是 our-block-theme 或带空格),banner.jsedit/save 分别指向命名的函数 EditComponent/SaveComponent,而不是像之前插件章节那样用匿名函数
  • EditComponent() 直接照抄传统主题 front-page.php 里 Banner 区块的 HTML,手动转成 JSX 需要做的调整:
    • classclassName
    • 内联样式从 CSS 字符串改成 JS 对象:style="background-image: url(...)"style={{backgroundImage: "url(...)"}}——注意 CSS 属性名要改驼峰式(background-imagebackgroundImage),且整个值依然是一个字符串("url('...')"),只是最外层从「HTML 属性字符串」换成了「JS 对象里的一个键值对」
    • 原本用 PHP 动态生成的图片路径,这一讲先硬编码成一个写死的路径(/wp-content/themes/fictional-block-theme/images/library-hero.jpg)——先让静态效果跑起来,后面几讲才会做成真正可以在编辑器里上传/选择图片的动态版本
    • 原本 PHP 输出的链接 href 也先换成占位符 #
  • SaveComponent() 暂时只返回占位文字<p>This is from our block.</p>)——因为这一讲的重点是先让编辑器里的显示效果正确,save 决定的是保存到数据库/前台最终渲染的内容,这一讲还不处理这部分,后续会回来完善
  • 把传统主题的整套资源文件夹搬进新主题build/css/images/inc/template-parts/src/ 这几个子文件夹,以及 functions.phppackage.json 两个文件,直接从 fictional-university(传统主题)复制到 fictional-block-theme(新的 Block Theme)——这些文件夹装着已经写好的完整 CSS 编译产物、图片资源、REST 端点、模板局部文件、前端 JS 源码等,不需要重新写一遍
  • package.json 里的依赖是「延续既有主题的选择」,不是这个新项目本身必需的:像 @glidejs/glide(首页幻灯片用)、axios(发请求用)这些第三方包,是因为要继续用传统主题已经写好的这些功能——如果是从零开始一个新项目,理论上只需要 @wordpress/scripts 这一个依赖
  • functions.php 依然是 Block Theme 的核心:从传统主题搬过来的 functions.php 里原本的 hook/filter(各种 add_action/add_filter)全部原样保留——FSE/Block Theme 并没有让 PHP 退场,functions.php 在两种主题类型里的作用完全一样
  • package.jsonstart 脚本要手动列出两个入口文件src/index.js(原本传统主题前端脚本的入口)和 our-blocks/banner.js(新的 Block 编辑器脚本),跟之前插件章节学过的「多构建入口」写法一致;这一讲提到暂时先把 build 任务从 package.json 删掉,只留 start(开发模式),方便先专注调试
  • wp_register_script('bannerBlockScript', ..., array('wp-blocks', 'wp-editor')):注册这个 Block 编辑器要用的 JS 资源,依赖数组里的 wp-blocks/wp-editor 表示这份脚本运行前需要先加载好 WordPress 提供的这两个基础包
  • register_block_type("ourblocktheme/banner", array('editor_script' => 'bannerBlockScript')):只声明了 editor_script,没有 render_callback——因为目前 save 函数还只是占位文字,前台渲染逻辑留到后面几讲处理
  • add_theme_support('editor-styles') + add_editor_style(array(...))——把整个网站的全局 CSS 一次性引入编辑器:跟独立插件 Block(比如「Are You Paying Attention」)为每个 Block 精心拆分出一份专属精简 CSS 的做法不同——这里选择偷懒直接把已经写好、给整个传统主题用的完整全局样式表(包括 Google Fonts 地址和两份编译产物 style-index.css/index.css)整个塞进编辑器,因为反正传统主题的这份 CSS 本来就是完整可用的,没必要为了「插件式精简加载」重新拆分一遍——这个取舍只适合「主题即网站」这种场景,如果是要发布给很多人用的独立 Block 插件,还是应该按需精简加载

Hook / Function 速查

名称类型用途
wp_register_script($handle, $url, $deps)WP 内建 function注册 Block 编辑器要用的 JS 资源
register_block_type($名字, array('editor_script' => ...))WP 内建 function注册自定义 Block 类型,只声明编辑器脚本、暂不含渲染回调
add_theme_support('editor-styles')WP 内建 function启用「允许自定义编辑器样式」这个主题特性
add_editor_style(array($样式路径...))WP 内建 function把指定的一批 CSS 文件(含外部字体地址)加载进 Block 编辑器界面
get_stylesheet_directory_uri()WP 内建 function获取当前主题目录的完整 URL,拼接静态资源路径常用

常见坑

  • JSX 里内联样式还沿用 CSS 字符串写法(style="...")而不是 JS 对象(style={{...}})——JSX 不接受字符串形式的 style 属性
  • CSS 属性名在 JS 对象里忘记转成驼峰式(比如依然写 background-image 而不是 backgroundImage)——JSX 无法识别、样式不会生效
  • 独立分发的插件 Block 也照搬「整个引入全局 CSS」这种做法——会让编辑器加载大量跟这个 Block 完全无关的样式,只有「主题即网站」这种场景才适合这样偷懒
  • package.jsonstart 脚本忘记把新的 Block 入口文件也列进去——@wordpress/scripts 默认只处理 src/index.js,新建的 our-blocks/banner.js 不会被编译

[截图:Gutenberg 编辑器里插入 Banner Block 后的样子,背景图/大中小标题/按钮的视觉效果已经跟前台传统主题一致]


延伸 / 后续讲座会用到

下一讲要让这个 Banner Block 真正支持在编辑器里点击文字直接编辑、上传/选择自定义背景图。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 28, EP179, EP180