搜索全站
主题 12 分钟阅读

子主题中正确加载父主题样式:wp_enqueue_style 依赖与版本控制

启用子主题后站点变裸、改了样式不生效、父主题升级后 CSS 还是旧的?问题多半出在 wp_enqueue_style 的依赖与版本号上。本文讲清四种加载写法、优先级与缓存控制的正确做法。

U
UFO_PF 2023-04-20 · 阅读 12 分钟
子主 主题
阅读提示
本文约 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」面板,它会列出加载顺序与依赖关系。

常见踩坑

  1. 只注册了子主题样式,没加载父样式:页面裸奔。
  2. 依赖句柄写错了名字:依赖不存在的句柄时,WordPress 会把依赖当作「已满足」,顺序失控,表现为覆盖规则时灵时不灵。
  3. 优先级太低:子主题 functions.php 先加载,若在优先级 10 就注册依赖父主题的句柄,而父主题在它之后才注册,依赖失效——把子主题的 add_action 优先级调到 20 更稳。
  4. 版本号写死:改了 CSS 客户端不更新,用户投诉「我这边还是旧的」。
  5. 重复加载同一份父样式:父主题已注册,子主题又注册一套,页面出现两份相同 CSS,体积翻倍。
  6. 案例:某站在 CDN 上配了「忽略查询字符串」的缓存规则,而子主题用的是 filemtime() 版本号,结果 CSS 更新后 CDN 仍返回旧文件,客服收到一堆「排版乱了」的反馈。最终改为发布时手动递增主题 Version,并在部署脚本里主动刷新 CDN 缓存——版本号方案必须和缓存策略配套设计。
  7. get_template_directory_uri() 与 get_stylesheet_directory_uri() 用混:在子主题里想引用自己的资源却引到了父主题目录,改了半天没反应。
  8. 把样式注册挂在 init 或 wp_head:必须用 wp_enqueue_scripts,否则部分页面(如登录页)不加载,也可能破坏依赖排序。

总结

正确的加载模板就四条:父样式用 get_template_directory_uri()、子样式用 get_stylesheet_uri()、子样式的依赖数组填父样式句柄、版本号用父/子主题版本或 filemtime()。若父主题有自己的句柄,子主题的 add_action 优先级给到 20。做完验证一次输出顺序,再清一次 CDN 与浏览器缓存。至于「改了样式还是不生效」的另一半原因——选择器权重与模板覆盖——可以分别参考 子主题覆盖模板文件规则 与 子主题标准制作步骤。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。