本文约 3,941 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:打开父主题,发现根本没有 header.php
传统主题做子主题的第一反应是:复制 header.php、footer.php 过来改。但当你打开一个区块主题(如 Twenty Twenty-Four、Twenty Twenty-Five)时,会发现里面没有这些 PHP 模板文件,只有 templates/index.html、parts/header.html 这类 HTML 文件,style.css 里也只有一段头注释,几乎没有实际样式。
于是问题来了:块主题的子主题到底靠什么工作?样式写在 theme.json 还是 style.css?为什么我在子主题里改了 parts/header.html,前台纹丝不动?这三个问题是区块主题(FSE)二次开发最常见的卡点,本质是对”文件—数据库—全局样式”三层来源的优先级不清楚。
原理分析:三层来源与优先级
块主题的模板渲染由核心的 get_block_template() / locate_block_template() 负责(位于 wp-includes/block-template.php)。模板有两个来源:主题目录下的 templates/ 与 parts/ 文件,以及数据库里 wp_template、wp_template_part 两种文章类型。在站点编辑器里做过的修改会写入数据库,且数据库版本优先于主题文件——这正是”改了文件没反应”的根源。
样式则主要来自 theme.json,核心通过 wp_get_global_stylesheet() 把 settings 与 styles 编译成内联 CSS 输出,style.css 在块主题里多数只承担元信息声明。子主题的 theme.json 会与父主题做深度合并:子主题声明的字段覆盖父主题同名字段,预置项(如调色板、字号)按 slug/name 归并。
| 改动类型 | 放置位置 | 优先级说明 |
|---|---|---|
| 整体版式(首页/单页) | 子主题/templates/*.html |
子主题文件 > 父主题文件,但均低于数据库自定义模板 |
| 页头页脚区块 | 子主题/parts/*.html |
同上 |
| 颜色、字号、间距 | 子主题/theme.json 的 settings/styles |
父子深度合并,子主题覆盖同名键 |
| 少量补充样式 | 子主题/style.css 或自定义 CSS |
作为常规样式加载,命中同一选择器时按加载顺序决定 |
| 功能代码 | 子主题/functions.php |
与经典主题一致,子主题先于父主题加载 |
保姆级实操
- 建子主题目录并写
style.css:头部必须有Template:指向父主题目录名,块主题同样依赖这个字段识别父子关系。 - 写
theme.json:只声明你要覆盖的部分,不要整份复制父主题配置,否则父主题升级后的改进会被你的旧副本锁死。 - 覆盖模板文件:从父主题
templates/复制index.html(或single.html)到子主题同路径再改;需要覆盖页头页脚时,在站点编辑器里先”清除自定义修改”,否则文件不会生效。 - 确认数据库里没有同名自定义模板:用 WP-CLI 列出
wp_template与wp_template_part,有残留就删除后重新加载。 - 功能扩展照旧:
functions.php里照常add_theme_support()、注册样式脚本,块主题并不排斥 PHP。 - 核对主题状态:用
wp theme list确认父主题存在且子主题状态为 active,父主题缺失会直接报错。
/*
Theme Name: TT4 Child
Template: twentytwentyfour
Version: 1.0.0
Text Domain: tt4-child
*/
/* 块主题的 style.css 通常只放真正需要额外加载的样式 */
.wp-block-navigation__responsive-container.is-menu-open {
background-color: #1b1b1b;
}
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"appearanceTools": true,
"color": {
"palette": [
{ "slug": "brand", "color": "#0f6fff", "name": "品牌蓝" },
{ "slug": "ink", "color": "#1b1b1b", "name": "墨黑" }
]
},
"typography": {
"fontSizes": [
{ "slug": "small", "size": "0.9rem", "name": "小" },
{ "slug": "large", "size": "1.75rem", "name": "大" }
]
},
"layout": {
"contentSize": "760px",
"wideSize": "1180px"
}
},
"styles": {
"elements": {
"link": { "color": { "text": "var(--wp--preset--color--brand)" } }
}
}
}
# 列出数据库里被站点编辑器改动过的模板/模板部件(这是"改文件无效"的元凶)
wp post list --post_type=wp_template --fields=ID,post_name,post_title
wp post list --post_type=wp_template_part --fields=ID,post_name,post_title
# 确认父子主题状态
wp theme list
常见踩坑
- 以为父主题的
style.css内容会被自动继承——块主题不排队父主题样式表,需要自己wp_enqueue_style引入。 - 站点编辑器里改过页头,之后在子主题
parts/header.html改文字完全无效,因为数据库版本优先。 theme.json整份复制父主题,父主题升级后新增的预置项被旧配置屏蔽。version字段写错或$schema指向过旧,导致部分新属性不生效。- 案例:某站在站点编辑器里把页头导航改成了两行布局,随后开发人员在子主题
parts/header.html反复调整、清缓存、换服务器都没效果。最后用wp post list --post_type=wp_template_part才发现数据库里躺着一条自定义记录,删除后文件修改立刻生效。 - 父主题被删掉或目录名与
Template:不一致,前台直接报 “The theme directory … does not exist”。 - 在
theme.json里写custom样式却忘了同步settings.custom,编辑器里看不到对应的控制面板。
总结
块主题子主题的三条判断线:文件 vs 数据库(数据库优先)、theme.json vs style.css(全局设置走前者,补丁样式走后者)、子主题 vs 父主题(深度合并,子覆盖父)。行动清单:① 先查数据库里有没有自定义模板;② theme.json 只写差异;③ 需要父主题样式就显式排队;④ 升级父主题后复查 theme.json 字段是否仍兼容。想系统理解模板查找机制的读者可看 WordPress 模板层级,子主题基础写法见 WordPress 子主题制作,样式入口的选择也可参考 主题自定义 CSS 放置位置。
