搜索全站
主题 14 分钟阅读

自己维护WordPress主题:版本管理与变更记录规范流程

自己维护的主题改了三年,出问题时说不清改了什么,是典型的版本管理缺失。本文给出 Git 分支模型、语义化版本号、style.css 版本头、changelog 模板与更新回滚的完整流程。

U
UFO_PF 2025-11-27 · 阅读 14 分钟
自己 主题
阅读提示
本文约 5,515 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:改了什么?没人说得清

一个自建主题维护了三年,某天首页样式突然错位。三个人分别回忆:设计师说上周改过卡片间距,后端说上个月动过 functions.php 的查询,运营说前天在「自定义 → 额外 CSS」里加了一段。现场翻服务器文件,主题目录里的 style.css 版本号还写着 1.0.0——从建站那天起就没变过。没有任何记录,只能靠二分法逐段注释代码。

这不是技术问题,是工程习惯问题。主题一旦进入「自己长期维护」的状态,它就从一份交付物变成了一个软件项目,需要版本号、需要变更记录、需要可回滚。WordPress 本身对这块很宽容(它只要求 style.css 头部声明版本),宽容的另一面是没有约束——全靠自己建规矩。

好消息是,这套规矩不需要重量级工具。一个 Git 仓库、一个 CHANGELOG.md、一条「改版本号的规则」,就能覆盖 90% 的场景。

原理分析:WordPress 的版本号在哪、怎么用

WordPress 读取主题版本有两个来源:

来源 位置 用途
style.css 头部 Version: 主题根目录 style.css 后台显示、缓存 busting 基础值
wp_get_theme()->get('Version') 运行时读取 常用于 wp_enqueue_style() 第 4 参数
主题选项里的 db version 自定义 option 触发升级例程
Git tag 仓库 人类可读的发布点

最常见的用法是把版本号当缓存破坏参数:wp_enqueue_style( 'main', $url, array(), wp_get_theme()->get('Version') )。版本号一变,浏览器就会重新拉取 CSS/JS。这意味着版本号不只是记录,它直接影响线上行为——版本号不改,用户可能一直拿着旧缓存。

版本号规则建议直接采用语义化版本(SemVer):主版本.次版本.修订号。破坏性变更(模板文件结构变、钩子签名变、需要用户手动操作)进主版本;新增功能且向后兼容进次版本;纯修 bug 进修订号。WordPress 自身以前不是 SemVer,但主题是独立项目,用 SemVer 没有冲突。

保姆级实操

步骤 1:初始化仓库与目录约定

cd wp-content/themes/my-theme
git init
git branch -M main
echo "node_modules/"       > .gitignore
echo "*.log"              >> .gitignore
echo ".DS_Store"          >> .gitignore
echo "/dist/*.map"        >> .gitignore
git add -A && git commit -m "chore: init theme baseline"

约定:main 永远是可发布状态;改动都在 feat/xxx、fix/xxx、chore/xxx 分支;合并进 main 时打 tag。

步骤 2:把版本号写进 style.css 头

/*
Theme Name: Mimangfei Base
Theme URI: https://mimangfei.com/
Author: Mimangfei
Author URI: https://mimangfei.com/
Description: 自建长期维护基线主题
Version: 1.4.0
Requires at least: 6.4
Tested up to: 6.8
Requires PHP: 8.0
Text Domain: mimangfei-base
*/

Tested up to 要跟着每次 WordPress 大版本更新同步改,这是 主题与WordPress新版本的前瞻性适配 里提到的兼容声明,也会在后台更新页显示。Version 每次发布必须递增,这是缓存 busting 的开关。

步骤 3:建立 changelog 模板

主题根目录建 CHANGELOG.md,固定使用 Keep a Changelog 的格式:

# Changelog

本文件记录 Mimangfei Base 主题的所有显著变更。
版本号遵循语义化版本规范。

## [1.4.0] - 2026-10-10

### Added
- 产品中心模板 `template-parts/content-product.php`,支持规格表格
- `theme.json` 新增 `spacingScale` 自定义间距档位

### Changed
- 移动端导航热区提升至 44px(WCAG 2.2 SC 2.5.5)
- `mmf_enqueue_assets()` 改为按页面类型条件加载脚本

### Fixed
- 修复归档页第二页 `pre_get_posts` 未排除 `product` 类型导致分页 404
- 修复 `esc_attr()` 缺失导致的属性上下文 XSS(见 header.php:47)

### Deprecated
- `mmf_get_product_meta()` 将在 2.0.0 移除,请改用 `mmf_get_product_spec()`

### Removed
- 移除对 `wp_get_sites()` 的调用(WordPress 6.1 已删除该函数)

六个分类(Added / Changed / Fixed / Deprecated / Removed / Security)覆盖绝大多数改动,写不出分类的改动说明它太小,可以合并到下一条。

步骤 4:自动生成 changelog 的前半段

规范化提交信息后,changelog 可以半自动生成:

# 提交信息约定:type(scope): subject
git commit -m "fix(pagination): 排除 product 类型避免归档分页 404"
git commit -m "feat(product): 新增规格表格模板"

# 导出两个 tag 之间的提交列表
git log --pretty=format:"- %s (%h)" v1.3.0..v1.4.0

把输出粘进 CHANGELOG 对应分类,人工补上为什么要改。自动化的部分是不遗漏,人工的部分是说清意图。

步骤 5:需要数据库迁移时写升级例程

主题改了选项结构、改了自定义表名,必须有升级逻辑,否则老站点更新后直接出错:

<?php
define( 'MMF_THEME_DB_VERSION', '3' );

add_action( 'after_switch_theme', 'mmf_theme_activate' );
function mmf_theme_activate() {
    if ( get_option( 'mmf_db_version' ) !== MMF_THEME_DB_VERSION ) {
        mmf_theme_upgrade();
    }
}

add_action( 'init', 'mmf_maybe_upgrade' );
function mmf_maybe_upgrade() {
    if ( get_option( 'mmf_db_version' ) !== MMF_THEME_DB_VERSION ) {
        mmf_theme_upgrade();
    }
}

function mmf_theme_upgrade() {
    $current = (int) get_option( 'mmf_db_version', 0 );

    if ( $current < 1 ) {
        // 1: 引入默认设置
        add_option( 'mmf_settings', array( 'layout' => 'grid', 'per_page' => 12 ) );
    }

    if ( $current < 2 ) {
        // 2: 把旧的单选项合并进数组
        $old = get_option( 'mmf_layout_mode' );
        if ( $old ) {
            $s = get_option( 'mmf_settings', array() );
            $s['layout'] = sanitize_key( $old );
            update_option( 'mmf_settings', $s );
            delete_option( 'mmf_layout_mode' );
        }
    }

    if ( $current < 3 ) {
        // 3: 为产品 CPT 补 rewrite 规则,需刷新固定链接
        flush_rewrite_rules();
    }

    update_option( 'mmf_db_version', MMF_THEME_DB_VERSION );
}

这种「逐级 if」的写法比 switch 更好维护:从 1 升到 3 会自动经过 2,不会出现跳级漏迁移。

步骤 6:发布与回滚

# 发布:改 style.css 的 Version,写 CHANGELOG,打 tag
git checkout main
git pull --ff-only
git tag -a v1.4.0 -m "1.4.0 产品中心模板 + 移动端热区"
git push origin main --tags

# 回滚:先回滚文件,再回滚数据
git checkout v1.3.0

回滚要注意:代码可以 git checkout 回上一版,但数据库迁移做过的事回不去。flush_rewrite_rules()、选项结构变更这类操作,需要写对应的 down 逻辑或至少在 CHANGELOG 里标明「此版本不可逆」。

步骤 7:上线前的固定检查清单

每次发布前三件事:① style.css 的 Version 已递增;② CHANGELOG 有对应条目;③ 预发布环境打开 WP_DEBUG_LOG 跑一遍主要页面(参考主题调试的标准做法),日志里无新增 deprecated 与 PHP notice。

常见踩坑

  1. 改了代码不改版本号:用户浏览器缓存旧 CSS,前台看起来「改了没生效」,反复刷新无效,最后被当成灵异事件。
  2. 直接在生产主题目录里改文件:没有版本控制,改错了只能靠记忆恢复。至少要做每日文件备份。
  3. CHANGELOG 只写「优化若干」:出事回查时毫无价值。每条要能定位到文件与动机。
  4. flush_rewrite_rules() 写在 init 上:每次请求都刷新,性能极差,正确做法是只在激活/升级时调用一次。
  5. 案例: 某主题 2.0 版本把自定义字段从 product_price 改名为 _product_price(加下划线变成隐藏字段)。代码更新后前台全部读不到价格,显示为 0。因为 CHANGELOG 里只在「Changed」写了「优化字段命名」,没有标注需要数据迁移,运维按常规流程发布后才发现,回滚代码期间站点展示了两小时的错误价格。教训:涉及数据结构变更必须在 CHANGELOG 里单独标「需迁移」,并提供迁移脚本。
  6. tag 打在错误的提交上:git tag 默认打在当前 HEAD,发布流程里应先 git pull --ff-only 确认分支最新再打。
  7. 子主题与父主题版本号混用:wp_get_theme() 在子主题下返回子主题版本,若用父主题版本做缓存 busting,子主题改动不会刷新缓存。应统一用 wp_get_theme()->get('Version') 并确保改的是当前启用主题。

总结

自己维护主题的版本管理,最低可行配置是四件套:style.css 里的语义化版本号(每次发布必改,兼作缓存 busting)、一个按 Added/Changed/Fixed 分类的 CHANGELOG.md、Git tag 作为发布点、以及带版本号常量的数据库升级例程。

判断做得是否到位的标准只有一条:任意一次线上故障,能否在五分钟内定位到「哪个版本引入的、改了哪几行、能否安全回滚」。做不到就说明记录还不够细。这套规范与 主题二次开发的成本评估 配合使用效果最好——维护成本能不能量化,取决于变更记录是否完整;而评估后决定重构时,也要先按 主题迁移到新主题时内容保全方案 规划好数据迁移。

U
UFO_PF作者

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

留下你的想法

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