本文约 4,759 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:一行依赖没写,样式就反了
子主题最常见的翻车现场:启用之后前台变成没有样式的纯 HTML——因为子主题不会自动加载父主题的 style.css。照着教程补了一段 wp_enqueue_style 之后,样式回来了,但新问题出现:你在子主题里写的覆盖规则不生效,浏览器里父主题的 CSS 反而排在后面。
原因就在于 wp_enqueue_style() 的第三个参数(依赖数组)。WordPress 的样式输出顺序不是「谁先注册谁先输出」,而是按依赖关系拓扑排序。不写依赖,两个样式表的输出顺序其实是「按注册顺序」,而子主题的 functions.php 虽然先加载,父主题的样式句柄可能注册得更早,于是父样式排在后面,把你的覆盖规则按同权重「后来者胜」的规则压掉。
原理分析:加载顺序、依赖与版本三件事
顺序。 wp_enqueue_style( $handle, $src, $deps, $version, $media )。$deps 里填父主题样式的句柄,就明确告诉 WordPress:「子样式必须在父样式之后输出」。
版本。 第四个参数是查询字符串(如 ?ver=1.0.0),用于缓存失效。不写版本,WordPress 会用自己的版本号,导致你改了 CSS 但用户浏览器/CDN 还拿旧文件。写死 1.0.0 同样有问题——改了样式要手动改版本号。最佳实践是用文件修改时间 filemtime(),改了文件版本号自动变。
句柄。 第一个参数是全局唯一的句柄(handle)。如果父主题已经用 astra-theme-css 注册了主样式,你在子主题里再注册一个 parent-style 指向同一个 style.css,会输出两份相同 CSS。正确做法是查到父主题句柄并作为依赖,或者用 wp_dequeue_style 先移除再自己注册。
还要区分两个 URI 函数:
| 函数 | 指向 |
|---|---|
get_stylesheet_directory_uri() |
子主题目录(当前启用的) |
get_template_directory_uri() |
父主题目录(永远是父) |
get_stylesheet_uri() |
子主题 style.css 的完整 URL |
在子主题里写 get_template_directory_uri() . '/style.css' 拿的是父主题样式;写 get_stylesheet_uri() 拿的是子主题自己的样式表。
保姆级实操
第一步:基础写法(父主题主样式是 style.css)
<?php
add_action( 'wp_enqueue_scripts', 'astra_child_enqueue_styles' );
function astra_child_enqueue_styles() {
wp_enqueue_style(
'astra-parent-style',
get_template_directory_uri() . '/style.css',
array(),
wp_get_theme( get_template() )->get( 'Version' )
);
wp_enqueue_style(
'astra-child-style',
get_stylesheet_uri(),
array( 'astra-parent-style' ),
wp_get_theme()->get( 'Version' )
);
}
wp_get_theme( get_template() ) 取父主题对象,用它的 Version 作为父样式版本号——父主题升级后版本号变化,浏览器自动拉新文件。
第二步:父主题有自己的样式句柄时
先查清楚:
grep -rn "wp_enqueue_style\|wp_register_style" wp-content/themes/astra/inc/*.php | head -20
假设找到 astra-theme-css,子主题写法:
add_action( 'wp_enqueue_scripts', 'astra_child_enqueue_styles', 20 );
function astra_child_enqueue_styles() {
wp_enqueue_style(
'astra-child-style',
get_stylesheet_uri(),
array( 'astra-theme-css' ),
wp_get_theme()->get( 'Version' )
);
}
只需注册子主题样式,父主题自己会加载。这里把优先级设为 20(大于默认 10),确保父主题已经注册了它的句柄——依赖一个尚未注册的句柄,WordPress 不会自动等待,而是直接输出。这是「依赖写了却还是顺序错」的常见原因。
第三步:用 filemtime 做缓存控制
function mimangfei_asset_version( $relative_path ) {
$file = get_stylesheet_directory() . $relative_path;
return file_exists( $file ) ? filemtime( $file ) : wp_get_theme()->get( 'Version' );
}
add_action( 'wp_enqueue_scripts', 'astra_child_styles' );
function astra_child_styles() {
wp_enqueue_style(
'astra-child-style',
get_stylesheet_uri(),
array( 'astra-parent-style' ),
mimangfei_asset_version( '/style.css' )
);
}
注意:filemtime() 每次请求都要读一次文件 stat,高流量站点建议配合对象缓存(用 wp_cache_get/set 缓存结果),或直接沿用主题版本号。
第四步:RTL(从右到左语言)的处理
add_action( 'wp_enqueue_scripts', 'astra_child_styles_rtl' );
function astra_child_styles_rtl() {
wp_style_add_data( 'astra-child-style', 'rtl', 'replace' );
}
'replace' 表示 RTL 时加载 rtl.css 替换主样式(也可以设 'add',在后面追加)。子主题需要提供自己的 rtl.css,否则 is_rtl() 为真时会去加载不存在的文件。
第五步:只在某些页面加载额外样式
add_action( 'wp_enqueue_scripts', 'mimangfei_conditional_styles' );
function mimangfei_conditional_styles() {
if ( is_page_template( 'page-templates/landing.php' ) ) {
wp_enqueue_style(
'mimangfei-landing',
get_stylesheet_directory_uri() . '/assets/css/landing.css',
array( 'astra-child-style' ),
mimangfei_asset_version( '/assets/css/landing.css' )
);
}
}
依赖挂在子主题样式之后,保证 landing.css 能覆盖通用规则。
第六步:不要用 @import
/* 不推荐:阻塞渲染、无法并行下载、被部分 CDN 优化移除 */
@import url( "../astra/style.css" );
@import 写在 CSS 文件开头会串行加载:浏览器得先下载子主题 style.css,解析到 @import 才去下载父样式,白白多一个 RTT。用 wp_enqueue_style 的写法两个文件并行下载。
第七步:验证输出顺序
在前台查看源码,或用命令行快速确认:
curl -s https://example.com/ | grep -o 'astra[a-z-]*style[^"]*' | head
正确顺序应该是父样式在前、子样式在后。也可以用 Query Monitor 的「Scripts & Styles」面板,它会列出加载顺序与依赖关系。
常见踩坑
- 只注册了子主题样式,没加载父样式:页面裸奔。
- 依赖句柄写错了名字:依赖不存在的句柄时,WordPress 会把依赖当作「已满足」,顺序失控,表现为覆盖规则时灵时不灵。
- 优先级太低:子主题
functions.php先加载,若在优先级 10 就注册依赖父主题的句柄,而父主题在它之后才注册,依赖失效——把子主题的add_action优先级调到 20 更稳。 - 版本号写死:改了 CSS 客户端不更新,用户投诉「我这边还是旧的」。
- 重复加载同一份父样式:父主题已注册,子主题又注册一套,页面出现两份相同 CSS,体积翻倍。
- 案例:某站在 CDN 上配了「忽略查询字符串」的缓存规则,而子主题用的是
filemtime()版本号,结果 CSS 更新后 CDN 仍返回旧文件,客服收到一堆「排版乱了」的反馈。最终改为发布时手动递增主题 Version,并在部署脚本里主动刷新 CDN 缓存——版本号方案必须和缓存策略配套设计。 get_template_directory_uri()与get_stylesheet_directory_uri()用混:在子主题里想引用自己的资源却引到了父主题目录,改了半天没反应。- 把样式注册挂在
init或wp_head:必须用wp_enqueue_scripts,否则部分页面(如登录页)不加载,也可能破坏依赖排序。
总结
正确的加载模板就四条:父样式用 get_template_directory_uri()、子样式用 get_stylesheet_uri()、子样式的依赖数组填父样式句柄、版本号用父/子主题版本或 filemtime()。若父主题有自己的句柄,子主题的 add_action 优先级给到 20。做完验证一次输出顺序,再清一次 CDN 与浏览器缓存。至于「改了样式还是不生效」的另一半原因——选择器权重与模板覆盖——可以分别参考 子主题覆盖模板文件规则 与 子主题标准制作步骤。
