本文约 4,430 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:直接改父主题,迟早要还
「在 functions.php 末尾加段代码」「把 single.php 里那行标题换掉」——直接改父主题是最快的改法,也是最贵的一种。父主题一升级,你的改动被覆盖,站点回到出厂状态;更糟的是有些改动升级后只覆盖一半,页面变成「新旧混合」的怪样子,排查成本远高于当初省下的时间。
子主题(Child Theme)就是为了解决这个问题:它继承父主题的全部功能与模板,你只写「不一样的那部分」。升级父主题时,子主题目录不受影响。对使用商业主题(Astra、GeneratePress、OceanWP、Kadence 等)的站点来说,子主题几乎是标配。
原理分析:WordPress 怎么把两个主题拼起来
WordPress 用两个常量区分父与子:
| 概念 | 父主题 | 子主题 |
|---|---|---|
| 目录常量 | TEMPLATEPATH / get_template_directory() |
STYLESHEETPATH / get_stylesheet_directory() |
| URI 常量 | get_template_directory_uri() |
get_stylesheet_directory_uri() |
| 识别依据 | 当前启用主题的 style.css | Template: 字段指向父主题目录名 |
识别的关键是 style.css 头部里的 Template:——它必须填父主题目录的文件夹名(如 astra),不是主题名(如「Astra」)。填错的表现是:后台里子主题显示为「损坏的主题」,提示「父主题缺失」。
加载顺序上有个重要细节:子主题的 functions.php 会先于父主题加载(详见 子主题与父主题 functions.php 执行顺序),而模板文件是子主题优先。样式表则不会自动加载——父主题的 style.css 需要你显式引入,这正是新手最容易踩的第一个坑:启用子主题后站点变成无样式的裸 HTML。
保姆级实操
第一步:建目录与 style.css
cd wp-content/themes
mkdir astra-child
wp-content/themes/astra-child/style.css:
/*
Theme Name: Astra Child
Theme URI: https://mimangfei.com/themes/astra-child
Description: Astra 的子主题,用于放置自定义样式与模板。
Author: 迷忙飞
Author URI: https://mimangfei.com
Template: astra
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: astra-child
*/
只有 Theme Name 和 Template 是必需的,其余按项目补。Template 的值与你看到的父主题目录名完全一致(区分大小写):
ls wp-content/themes | grep -i astra
第二步:写 functions.php 加载父主题样式
现代写法(推荐,支持依赖与缓存控制):
<?php
add_action( 'wp_enqueue_scripts', 'astra_child_enqueue_styles' );
function astra_child_enqueue_styles() {
$parent = wp_get_theme( 'astra' );
wp_enqueue_style(
'astra-parent-style',
get_template_directory_uri() . '/style.css',
array(),
$parent ? $parent->get( 'Version' ) : null
);
wp_enqueue_style(
'astra-child-style',
get_stylesheet_uri(),
array( 'astra-parent-style' ),
wp_get_theme()->get( 'Version' )
);
}
关键点:子主题样式的第三个参数(依赖)填父主题样式的句柄,保证 CSS 输出顺序是「父在前、子在后」,这样你的规则才能覆盖父主题的同名规则。
第三步:确认父主题样式句柄(进阶)
如果父主题已经用某个句柄注册了主样式,重复注册会输出两份。更好的做法是查清父主题的句柄,把子样式挂在它后面:
grep -rn "wp_enqueue_style" wp-content/themes/astra/functions.php | head
或者在运行时打印:
add_action( 'wp_print_styles', function() {
global $wp_styles;
if ( isset( $_GET['debug_styles'] ) ) {
echo '<pre>' . esc_html( implode( "\n", array_keys( $wp_styles->registered ) ) ) . '</pre>';
}
} );
拿到父主题句柄(如 astra-theme-css)后:
wp_enqueue_style(
'astra-child-style',
get_stylesheet_uri(),
array( 'astra-theme-css' ),
wp_get_theme()->get( 'Version' )
);
第四步:加截图(可选但强烈建议)
做一张 1200×900 的 screenshot.png 放进子主题目录。没有它,后台主题列表里显示为一个灰色占位块,多个主题并存时很难分辨。
第五步:启用并验证
wp theme activate astra-child
wp theme list
验证三件事:
- 前台样式是否正常(若「裸奔」,多半是
Template写错或父样式未加载); - 子主题的
style.css是否真的生效(临时加一条body { outline: 1px solid red; }测试); - 后台「外观 → 主题」里子主题是否显示为「活动」,且父主题标记为「父主题」。
第六步:放第一个模板覆盖
比如想改文章页,把父主题的 single.php 复制到子主题同路径再改:
cp wp-content/themes/astra/single.php wp-content/themes/astra-child/single.php
覆盖的具体规则(哪些文件能覆盖、get_template_part 的差异)见 子主题覆盖模板文件规则。
第七步:区块主题(FSE)子主题的特殊写法
若父主题是区块主题(如 Twenty Twenty-Four),子主题结构与经典主题不同:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{ "slug": "brand", "color": "#2563eb", "name": "品牌蓝" }
]
}
},
"styles": {
"elements": {
"link": { "color": { "text": "var(--wp--preset--color--brand)" } }
}
}
}
区块子主题的 theme.json 会与父主题合并(子主题的同名设置覆盖父主题),模板则放在 templates/ 与 parts/ 目录下按同名覆盖,不需要 functions.php 加载样式——全局样式由核心自动生成。
常见踩坑
Template填了主题名而不是目录名:后台提示父主题缺失,子主题无法启用。- 启用后站点无样式:只写了
style.css没写functions.php,父主题样式没被引入。 - 用
@import加载父样式:老教程常见写法,@import会阻塞渲染且无法并行加载,已被官方不推荐。 - 子样式覆盖不了父样式:依赖没写,输出顺序反了;或父主题用了更高优先级的选择器,需要提高子主题选择器权重。
- 改了子主题样式没生效:浏览器或 CDN 缓存了旧 CSS,版本号未更新。
- 案例:某企业站做了子主题,上线第二天首页样式全丢。排查发现
style.css里Template: Astra首字母大写,Windows 本地环境目录不区分大小写所以能用,部署到 Linux 服务器后匹配不到目录,WordPress 判定父主题缺失并回退到默认主题。改成小写astra后恢复——这类问题在本地永远测不出来。 - 把整个父主题复制进子主题:那就不是子主题了,等于维护两份代码,升级优势全部消失。
- 子主题目录名与父主题相同:目录冲突,
Template指向自己,出现循环引用。
总结
子主题的最小可用集合只有两个文件:style.css(含 Template)与 functions.php(加载父样式)。做完后按顺序验证:后台能识别、前台有样式、子主题 CSS 优先级更高。之后每加一个改动,都问自己一句「这个改动放在子主题里能不能实现」——能用钩子实现的绝不覆盖模板,能覆盖模板的绝不改父主题。加载样式的更多细节见 子主题加载父主题样式。
