本文约 8,228 字,预计阅读 21 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:第五个站点,还在重复第一次的工作
接手第五个客户的建站需求时,我发现自己在做和第一个站点完全相同的事:装同一套主题、改同一批 CSS 变量、加同一段 wp_head 清理代码、写同一个产品 CPT。前面四个站点的经验,一点都没沉淀下来——它们分散在四个主题目录里,格式各不相同,复制粘贴时还要逐个改函数名前缀。
问题出在没有「基线」。基线不是某个具体主题,而是一套你自己认可的最小可用起点:目录怎么分、变量怎么命名、哪些功能放主题哪些放插件、版本怎么管理。有了它,新站点只需要做「业务差异化」的部分,重复劳动被压缩到接近零。
对独立开发者和小团队来说,这条路的终点不是做出一个能卖的商业主题,而是做出一个「只有自己需要维护、但足够通用」的内部基线。它的代码量不需要大,关键是结构稳定、边界清晰。
原理分析:三种形态的边界在哪里
WordPress 主题的自定义有三种形态,成本和自由度差异很大:
| 形态 | 自由度 | 维护成本 | 适合阶段 |
|---|---|---|---|
| 子主题(Child Theme) | 低,只能覆盖父主题有的东西 | 中,需跟随父主题更新 | 1–3 个站点,快速交付 |
| 起始主题改造(_s / 块主题) | 高,一切自己控 | 中高,无父主题更新负担 | 3 个站点以上 |
| 从零写(style.css + index.php) | 最高 | 高,所有细节自己兜 | 有特殊架构需求 |
判断的关键维度是「父主题更新时你要做多少事」。子主题的痛点是模板覆盖——父主题一更新,你复制出去改过的 header.php 就得重新合并(这部分成本怎么量化,可以参考二次开发成本评估的思路)。起始主题则没有这个问题,因为你就是父主题。
另一个必须提前想清楚的边界:什么该放主题,什么该放插件。判断标准很简单——换主题后还需要的,放插件;换主题就不需要的,放主题。按这个标准,CPT 声明、自定义字段定义、短代码、业务相关的钩子都应该进插件;模板、样式、区块样式变体才属于主题。这条边界守住了,将来换主题的代价会小一个数量级。
技术选型上,2026 年的现实是:块主题(theme.json + 区块模板)是 WordPress 官方明确的方向,新项目建议直接上块主题;经典主题 + 子主题仍在大量存量站点上使用,维护类项目沿用即可。
保姆级实操
步骤 1:第一阶段,用子主题沉淀改动
cd wp-content/themes
wp scaffold child-theme mmf-base --parent_theme=twentytwentyfour \
--theme_name="Mimangfei Base" --author="Mimangfei" --author_uri=https://mimangfei.com/
生成的结构里,style.css 头部必须有 Template: twentytwentyfour 这一行,这是子主题唯一的身份标识。functions.php 里加载父主题样式的方式要用 wp_enqueue_style() 带父主题 handle:
<?php
add_action( 'wp_enqueue_scripts', 'mmf_child_enqueue' );
function mmf_child_enqueue() {
wp_enqueue_style(
'parent-style',
get_template_directory_uri() . '/style.css',
array(),
wp_get_theme( 'twentytwentyfour' )->get( 'Version' )
);
wp_enqueue_style(
'child-style',
get_stylesheet_directory_uri() . '/style.css',
array( 'parent-style' ),
wp_get_theme()->get( 'Version' )
);
}
这个阶段的核心纪律:只覆盖必要的模板文件,其余全用钩子。能写进 functions.php 的改动,就不要复制模板文件出来改。
步骤 2:把通用改动抽成「功能插件」
建一个不属于任何主题的功能插件,承载换主题也要带走的逻辑:
wp-content/plugins/mmf-core/
├── mmf-core.php
├── inc/
│ ├── post-types.php # CPT 与分类法
│ ├── shortcodes.php # 短代码兼容层
│ ├── meta.php # 自定义字段读取函数
│ └── cleanup.php # 前台资源精简
└── README.md
<?php
/**
* Plugin Name: MMF Core
* Description: 站点核心内容模型与通用逻辑,与主题解耦
* Version: 1.0.0
*/
require_once __DIR__ . '/inc/post-types.php';
require_once __DIR__ . '/inc/shortcodes.php';
require_once __DIR__ . '/inc/meta.php';
require_once __DIR__ . '/inc/cleanup.php';
这样切到任意主题,业务逻辑都不丢。
步骤 3:第二阶段,用起始主题建自有基线
Underscores(_s)是 Automattic 维护的经典主题起始模板,用 WP-CLI 可以直接生成:
cd wp-content/themes
wp scaffold _s mmf-base --theme_name="Mimangfei Base" \
--author="Mimangfei" --author_uri=https://mimangfei.com/ --activate
生成后按自己的习惯重排目录:
mmf-base/
├── style.css
├── theme.json # 块主题用;经典主题删掉
├── functions.php # 只做 require,不写逻辑
├── inc/
│ ├── setup.php # add_theme_support 与 register_nav_menus
│ ├── enqueue.php # 脚本样式
│ ├── template-tags.php # 模板辅助函数
│ └── blocks.php # 区块样式变体与模式
├── template-parts/
│ ├── content.php
│ ├── content-product.php
│ └── hero.php
├── patterns/ # 区块模式(块主题)
├── parts/ # 模板部件(块主题)
├── templates/ # 区块模板(块主题)
└── CHANGELOG.md
functions.php 保持极简,只负责装配:
<?php
define( 'MMF_THEME_VERSION', '1.0.0' );
require_once get_template_directory() . '/inc/setup.php';
require_once get_template_directory() . '/inc/enqueue.php';
require_once get_template_directory() . '/inc/template-tags.php';
require_once get_template_directory() . '/inc/blocks.php';
步骤 4:块主题路线,用 theme.json 做唯一配置源
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"appearanceTools": true,
"layout": { "contentSize": "720px", "wideSize": "1200px" },
"color": {
"palette": [
{ "slug": "brand", "color": "#1a5fd0", "name": "品牌蓝" },
{ "slug": "ink", "color": "#1c1f23", "name": "正文黑" },
{ "slug": "muted", "color": "#6b7280", "name": "辅助灰" },
{ "slug": "surface", "color": "#f5f7fa", "name": "浅底" }
]
},
"typography": {
"fluid": true,
"fontSizes": [
{ "slug": "small", "size": "0.875rem", "name": "辅助" },
{ "slug": "body", "size": "1.0625rem", "name": "正文" },
{ "slug": "large", "size": "1.5rem", "name": "小标题" },
{ "slug": "xl", "size": "2.25rem", "name": "大标题" }
]
},
"spacing": { "units": ["px", "rem", "em", "%"] }
},
"styles": {
"typography": { "fontFamily": "system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif" }
}
}
把颜色、字号、间距全部声明在这里,编辑器与前台自动同步,后续所有站点的视觉基线就统一了。中文字体栈里带上 PingFang SC 与 Microsoft YaHei 是必须的。
步骤 5:注册区块样式变体与模式,减少重复排版
<?php
add_action( 'init', 'mmf_register_block_patterns' );
function mmf_register_block_patterns() {
register_block_pattern_category( 'mmf', array(
'label' => 'MMF 布局',
) );
register_block_pattern( 'mmf/product-card', array(
'title' => '产品卡片',
'categories' => array( 'mmf' ),
'description' => '图文产品卡片,含标题、摘要与按钮',
'content' => '<!-- wp:group {"className":"mmf-card"} -->
<div class="wp-block-group mmf-card">
<!-- wp:image {"sizeSlug":"large"} /-->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">产品名称</h3><!-- /wp:heading -->
<!-- wp:paragraph --><p>一句话介绍产品核心能力。</p><!-- /wp:paragraph -->
<!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button">查看详情</a></div><!-- /wp:button --></div><!-- /wp:buttons -->
</div>
<!-- /wp:group -->',
) );
}
区块模式的 content 是块标记的序列化字符串,从编辑器里复制现成布局再包一层最稳妥,手写容易漏闭合注释。
步骤 6:必备的基线支撑声明
<?php
add_action( 'after_setup_theme', 'mmf_theme_setup' );
function mmf_theme_setup() {
load_theme_textdomain( 'mmf-base', get_template_directory() . '/languages' );
add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'html5', array(
'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', 'style', 'script',
) );
add_theme_support( 'wp-block-styles' );
add_theme_support( 'editor-styles' );
add_editor_style( 'assets/css/editor.css' );
register_nav_menus( array(
'primary' => '主导航',
'footer' => '页脚导航',
) );
add_image_size( 'mmf-card', 480, 320, true );
add_image_size( 'mmf-wide', 1200, 600, true );
}
这几项缺一会在后续踩坑:没有 title-tag 标题结构失控;没有 responsive-embeds 嵌入视频不自适应;没有 wp-body-open(模板里要调用)很多插件注入失效。
常见踩坑
functions.php里塞满业务逻辑:几百行堆在一起,后期无法拆分。一开始就按inc/分文件。- 子主题里复制模板文件成瘾:复制越多,父主题更新越痛。能用钩子解决的绝不复制。
get_template_directory()与get_stylesheet_directory()混用:子主题场景下前者指向父主题,后者指向子主题,加载子主题资源必须用后者。- 块主题里同时写
theme.json和大量自定义 CSS:两处定义颜色会导致编辑器与前台不一致。样式来源要唯一。 - 案例: 某团队把 CPT 声明写在自研主题里,三个站点共用同一套代码。后来要给其中一个站点换设计,发现换主题会让 400 多个产品页 404,只能把整套主题复制一份再改,从此维护成本翻倍。教训:CPT 必须在第一版就放进功能插件。
- 没有
load_theme_textdomain():后期做多语言或翻译时全部字符串要返工。 - 版本号写死在多个文件:应定义
MMF_THEME_VERSION常量统一引用,发版只改一处。
总结
主题基线的演进路线是:子主题(快速交付,严格控制模板覆盖)→ 功能插件(把换主题也要带走的东西抽出来)→ 起始主题自建基线(去掉父主题依赖)→ 块主题 + theme.json 统一配置源。
三个必须守住的边界:业务逻辑进插件、视觉与模板进主题、版本与变更记录跟代码走(版本号管理规范见 自己维护主题的版本管理与变更记录)。基线建成后,新站点的启动时间能从两三天压到几小时。后续做企业站时,可直接在此基础上按 企业站产品中心CPT搭建教程 扩展业务模块。
