WordPress 子主题开发
两个文件起步,把改动和升级彻底分开
子主题要解决的问题只有一个:让自定义改动在父主题更新后仍然活着。它不复杂,但细节错一处就不生效——Template 名字大小写不对、样式没声明依赖、函数加载顺序搞反,都是我们接手时最常见的现场。这一页把可直接用的代码和覆盖顺序一次讲透。
第一步:两个文件,先把 style.css 写对
Template 写错,子主题就不成立
改父主题当天一定最快,问题出在三个月后:主题推送安全更新,你要么不升、承担漏洞风险,要么升级、改动全部归零。子主题把这个二选一变成”都能要”。目录放在 wp-content/themes 下,目录名随意,但里面必须有 style.css 且头部注释满足识别规则。
为什么不在父主题上直接改
- 改动可继承:只放改过的文件,其余回落到父主题,八十个模板里你只覆盖一个,剩下七十九个照常更新。
- 回滚成本低:停用子主题,站点立刻回到父主题原始状态,内容不丢;直接改父主题的站,出问题只能翻备份。
- 交付清晰:自定义集中在一个目录,动过哪些模板、加了哪些钩子、覆盖了哪些选择器,交接时一目了然。
可直接复制的最小 style.css
/*
Theme Name: Xinke Child
Theme URI: https://www.wordpressx.com/
Description: 信科网络子主题,仅存放样式覆盖与模板改动
Author: XINKE DIGITAL
Author URI: https://www.wordpressx.com/
Template: woodmart
Version: 1.0.0
Text Domain: xinke-child
*/
几个必须说清的点。Template 的值是父主题的目录名,不是 Theme Name,不区分显示名,且大小写敏感——父主题目录叫 woodmart,写成 WoodMart 后台就看不到子主题。Version 建议每次改动都加,用来给样式表打版本号,避免浏览器缓存旧 CSS。Text Domain 用于翻译,和加载语言包的代码保持一致即可。
只有这两个文件(style.css 与 functions.php)就能启用,缺 functions.php 也能启用,只是没法加入队逻辑。父主题的具体目录名可以在服务器上直接看,不同主题的目录命名规则不一样,参考 WoodMart 主题 这类商业主题时尤其要注意。
第二步:functions.php 里正确入队样式
依赖数组决定谁后加载
把子主题样式写进 style.css 还不够,必须让它排在父主题样式之后。最稳的做法是声明依赖句柄,而不是靠加载顺序碰运气。
父子样式入队模板
<?php
add_action( 'wp_enqueue_scripts', function () {
$parent_ver = wp_get_theme( get_template() )->get( 'Version' );
// 父主题样式:句柄自定,路径用 get_template_directory_uri()
wp_enqueue_style(
'xinke-parent',
get_template_directory_uri() . '/style.css',
array(),
$parent_ver
);
// 子主题样式:依赖上面的句柄,保证排在父主题之后
wp_enqueue_style(
'xinke-child',
get_stylesheet_directory_uri() . '/style.css',
array( 'xinke-parent' ),
filemtime( get_stylesheet_directory() . '/style.css' )
);
}, 20 );
三个细节容易忽略。一是 get_template_directory_uri 指向父主题,get_stylesheet_directory_uri 指向当前启用的子主题,两者不能混用。二是 filemtime 让版本号随文件修改时间变化,改完样式刷新即生效,不必手工改版本号;生产环境若文件权限受限,退回用固定版本号。三是若父主题自己已经入队了样式,你只需拿到它的句柄写进依赖数组,不必重复入队,重复会让同一份 CSS 加载两次。
查找父主题句柄的办法是搜父主题目录里的 wp_enqueue_style 调用,参数第一个就是句柄名。有些主题用 wp_register_style 注册后再挂载,同样以注册名为准。
第三步:搞清覆盖顺序,别用感叹号硬顶
四层优先级,从上往下找原因
覆盖不生效,九成是顺序或权重问题。按下面的顺序排查,比随手加 !important 有效得多。
| 层级 | 谁生效 | 怎么控制 |
|---|---|---|
| 文件加载顺序 | 后加载的样式覆盖先加载的 | 子主题样式声明依赖父主题句柄 |
| 选择器权重 | 权重高者胜,同权重看先后 | 用与父主题同级或略高的选择器,别滥加层级 |
| 内联样式 | 标签上的 style 属性优先级最高 | 改主题设置项,不要跟内联样式对打 |
| 模板文件 | 子主题同名文件覆盖父主题 | 按原相对路径复制,保持文件名一致 |
模板覆盖要注意层级:父主题的 single-product.php 在根目录,子主题就放根目录;父主题放在 templates/ 子目录,子主题也要放 templates/ 下同名路径,放错位置不会报错,只是不生效。函数层要记住一个反直觉的事实:子主题的 functions.php 先于父主题加载,所以不能在文件顶层直接调用父主题定义的函数,要挂到 after_setup_theme 且优先级设为 20 之后再调。
六个高频故障与处理路径
现象、原因、动作,一一对应
后台看不到子主题
Template 值写错或大小写不符,WordPress 认为父主题不存在,直接不列出。核对父主题目录名,逐字符比对。
样式完全没反应
多半是用了 @import 引父主题样式,或者根本没入队。@import 会串行阻塞、拖慢首屏,应在 PHP 里用 wp_enqueue_style 处理。
改动被父主题压过
父主题选择器权重更高。先用开发者工具看命中的规则来自哪个文件,再决定提高权重还是换更精确的选择器。
模板覆盖无效
路径层级不对,或父主题用 locate_template 走了自定义查找。确认文件相对路径一致,必要时用主题提供的钩子替代覆盖。
第五个坑是函数重名:父主题与子主题都定义了同名函数,站点直接白屏。稳妥写法是 if ( ! function_exists( ‘xxx’ ) ) 包一层。第六个是升级后失效:父主题改了模板结构或钩子名,子主题覆盖的旧文件仍在生效,但缺少新功能。每次父主题大版本更新后,都要重读过一遍被覆盖的文件。相关流程见 版本与插件更新策略。
子主题能做什么,不能做什么
边界清楚,才不会越写越乱
适合放进子主题
- 配色、字阶、间距、按钮等视觉覆盖
- 少量模板结构调整,如商品页加参数区
- 注册自定义文章类型、字段、短代码
- 挂载钩子修改输出,不动核心文件
- 给客户站点做的小型功能增强
不适合放进子主题
- 成套业务逻辑,应独立成插件,换主题不失效
- 大段重构的模板,维护成本高于直接定制
- 数据库结构变更,升级时容易与父主题冲突
- 第三方库的魔改,更新即失效
- 把父主题整个复制过来改,等于自建分支
判断标准很简单:这段代码换主题后还应不应该存在?应该,就写成插件;不应该,就放子主题。改动量超过父主题三成时,直接做定制主题更合理,路径见 WordPress 主题定制开发 与 WordPress 二次开发。
关于子主题的五个具体问题
不需要,也不应该。只放你实际改过的文件,其余全部回落到父主题。全量复制的子主题等于把父主题冻结在复制那一刻,之后的安全更新与功能改进都拿不到,还容易在升级时产生难以排查的错位。
说明加载顺序或权重没处理对。常见原因是子主题样式先于父主题加载,或者父主题用了更深的选择器。先声明依赖句柄解决顺序,再用同级选择器覆盖;仍不生效就查是否有内联样式。长期靠感叹号维护的站,半年后基本没人敢动。
不会替代,两份都会执行,且子主题的先执行、父主题的后执行。所以同名函数会冲突报错,必须用 function_exists 判断包一层。也因为先执行,子主题顶层拿不到父主题定义的函数,需要延后到 after_setup_theme 之后再调用。
不能直接沿用。Template 指向变了,模板路径和钩子名也大概率不同,样式选择器更是全变。可行的做法是把子主题里的业务逻辑抽成插件,视觉覆盖重写。这也是为什么建议把数据放自定义字段,而不是绑在某个主题的结构上。
文件层面几乎无影响,多一个 CSS 请求而已,HTTP/2 下可以忽略。真正影响速度的是你在子主题里加了多少东西:额外的字体、图标库、第三方脚本。控制住这些,子主题本身不会成为瓶颈。
手上的站改过父主题,正在等下一次更新爆雷
把小主题方案发来,我们帮你收拾干净
可以把改动点列出来,我们按样式、模板、函数三类拆进子主题,并做一次升级演练。邮箱 web@wordpressx.com,电话/微信 13280692153。