搜索全站
主题 10 分钟阅读

子主题覆写 WooCommerce 模板教程:自定义页面模板的正确做法

改了插件模板却怕升级丢改动?本文拆清 WordPress 页面模板与 WooCommerce 模板两套查找机制,给出子主题安全覆写的目录规范、钩子替代方案与版本核对方法。

U
UFO_PF 2024-07-22 · 阅读 10 分钟
子主 主题
阅读提示
本文约 3,680 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:改在插件目录里,一次升级全部归零

做外贸独立站的同学,大概都干过这样一件事:商品详情页想加一段”发货时效说明”,直接打开 wp-content/plugins/woocommerce/templates/single-product.php 改了两行,上线效果不错。结果某天 WooCommerce 自动更新,改动被覆盖,后台”WooCommerce → 状态 → 模板”还挂出一排红色提示,说模板版本过时。更尴尬的是,很多人从此不敢点更新,站点一直带着已知漏洞在线上跑。

正确做法是把插件模板”搬”到主题里再改。WordPress 自身有页面模板机制,WooCommerce 又额外提供了一套模板覆写机制,两者叠加时,新手极易搞混查找路径——文件放对了目录却不生效,八成是目录名或文件头不对。本文把这两套机制拆开讲,并给出只改一小段时的钩子替代方案。

原理分析:两套模板查找逻辑各走各的

WordPress 的模板层级是一条”从具体到通用”的回退链:单页优先找 page-{slug}.php、page-{id}.php,再找带 Template Name: 头注释的自定义页面模板,最后回退到 page.php、singular.php、index.php。查找时先扫子主题目录(get_stylesheet_directory()),没找到再扫父主题(get_template_directory())。

WooCommerce 是另一套逻辑:它默认在自己的 templates/ 目录取文件,但会通过 wc_get_template() / wc_get_template_part() 里的 woocommerce_locate_template 过滤器,让”主题目录/woocommerce/”下的同名文件优先。注意这里是主题目录,子主题优先于父主题。

你想改什么 文件应该放在 生效前提
给某个页面套单独版式 子主题根目录 template-xxx.php 文件头有 Template Name:
商品详情页 子主题/woocommerce/single-product.php 目录名必须是 woocommerce
商品列表里的单个卡片 子主题/woocommerce/content-product.php 同上,且被 wc_get_template_part 调用
商品分类归档页 子主题/woocommerce/archive-product.php 同上
面包屑、分页等小片段 子主题/woocommerce/global/、loop/ 对应子目录 相对路径需与插件内保持一致

保姆级实操

  1. 建目录:在 wp-content/themes/你的子主题/ 下新建 woocommerce/,拼写与大小写必须完全一致,Linux 服务器区分大小写。
  2. 复制而非新建:从 wp-content/plugins/woocommerce/templates/ 复制对应文件过来,保留原文件顶部的 if ( ! defined( 'ABSPATH' ) ) exit; 和 @version x.x.x 注释,只改你要动的那几行。版本注释丢了,后台会一直误报”模板过时”。
  3. 页面模板单独处理:在子主题根目录新建 PHP 文件,头部写模板名与适用的文章类型,保存后到页面编辑器右侧”模板”下拉里选择即可。
  4. 能挂钩子就别复制:如果只是追加一段内容,优先用 woocommerce_single_product_summary 这类 action 挂载,维护成本远低于复制整份模板(见下方代码)。
  5. 核对覆盖状态:改完去”WooCommerce → 状态 → 模板”,确认目标文件被标记为”已覆写”,且没有”需要更新”的版本提示。
  6. 清缓存再看效果:开了页面缓存的站点,务必先清一次缓存再验证,否则你会以为改动没生效。
<?php
/**
 * Template Name: 落地页-宽屏
 * Template Post Type: page
 *
 * @package lumen-mimangfei-child
 */

get_header();
?>
<main id="primary" class="site-main landing-wide">
    <?php
    while ( have_posts() ) :
        the_post();
        the_content();
    endwhile;
    ?>
</main>
<?php
get_footer();
<?php
// 子主题 functions.php:只追加一段发货说明,不复制整个模板
add_action( 'woocommerce_single_product_summary', 'mimangfei_ship_note', 25 );
function mimangfei_ship_note() {
    if ( ! is_product() ) {
        return;
    }
    echo '<p class="ship-note">下单后 48 小时内发货,支持 7 天无理由退换。</p>';
}

// 需要精确指定模板位置时,用过滤器接管查找结果
add_filter( 'woocommerce_locate_template', 'mimangfei_locate_template', 10, 3 );
function mimangfei_locate_template( $template, $template_name, $template_path ) {
    if ( 'single-product.php' === $template_name ) {
        $custom = get_stylesheet_directory() . '/woocommerce/single-product.php';
        if ( file_exists( $custom ) ) {
            return $custom;
        }
    }
    return $template;
}
# 正确的覆写目录结构(子主题)
wp-content/themes/lumen-mimangfei-child/
├── style.css
├── functions.php
├── template-landing-wide.php
└── woocommerce/
    ├── archive-product.php
    ├── single-product.php
    └── content-product.php

# 确认当前启用的主题与父子关系
wp theme list

常见踩坑

  • 目录名写成 WooCommerce、woocommerce-templates 或 templates,覆写静默失效,排查半天找不到原因。
  • 复制模板时删掉了 @version 注释,后台持续提示版本过时,误以为覆盖没成功。
  • 改完没清页面缓存,前台依旧输出旧模板。
  • 父主题和子主题各放一份同名文件,实际只有子主题那份生效,在父主题里改等于白改。
  • 案例:某站把 content-product.php 里的价格输出删掉换成自己拼的 HTML,一次 WooCommerce 升级后促销价逻辑变化,活动页全部显示原价,挂了两天才发现。正确做法是保留 woocommerce_template_loop_price 的调用,用 CSS 或 woocommerce_get_price_html 过滤器改呈现。
  • 只改了 single-product.php,但商品是可变商品,实际渲染走的是表单与变量分支,改动看不到。
  • 一边改插件目录、一边改子主题,两份文件并存时自己都分不清哪个在生效。

总结

判断标准很简单:改动范围小于一个函数,用 action/filter;需要重排一整块结构,才复制模板到 子主题/woocommerce/。落地清单:① 确认子主题已启用;② 复制时保留 ABSPATH 检查与 @version 注释;③ 改完到 WooCommerce 状态页核对;④ 每次插件升级后复查一次模板页;⑤ 上线前清一次缓存。子主题的创建方式可参考 WordPress 子主题制作,模板查找顺序的细节可延伸阅读 WordPress 模板层级,涉及商品页结构调整的也可结合 WooCommerce 模板覆写 一起看。

U
UFO_PF作者

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

留下你的想法

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