EP179-180. “主题内自建 Block:复用传统主题 CSS 到编辑器”
🔒 登录后可标记已读📌 说明: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’ll like it here.</h2>
<h3 className="headline headline--small">
Why don’t you check out the <strong>major</strong> you’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.json:start 脚本改成同时处理两个入口文件:
"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.js里edit/save分别指向命名的函数EditComponent/SaveComponent,而不是像之前插件章节那样用匿名函数EditComponent()直接照抄传统主题front-page.php里 Banner 区块的 HTML,手动转成 JSX 需要做的调整:class→className- 内联样式从 CSS 字符串改成 JS 对象:
style="background-image: url(...)"→style={{backgroundImage: "url(...)"}}——注意 CSS 属性名要改驼峰式(background-image→backgroundImage),且整个值依然是一个字符串("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.php、package.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.json的start脚本要手动列出两个入口文件: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.json的start脚本忘记把新的 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