本文约 6,336 字,预计阅读 16 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:从空白页到能用的主题,中间隔着二十个文件
新建一个 WordPress 主题,理论上只需要 style.css 和 index.php 两个文件。但真要交付,你得处理:header、footer、侧边栏、单篇文章、页面、归档、搜索结果、404、评论、分页、菜单、特色图、国际化……挨个从零写,光是搞清楚模板层级就要花几天。
Underscores(GitHub 上的 Automattic/_s)就是为了省掉这段重复劳动:它是一个「什么都齐了但什么都没设计」的主题骨架,模板层级完整、代码符合官方编码规范、带 Sass 源码和构建脚本、可访问性基础(跳转链接、screen-reader-text)也已就位。它的定位很明确——起点,不是成品。你要做的是三件事:精简掉用不上的部分、按设计稿补齐模板、把占位函数名换成自己的前缀。
原理分析:_s 的目录结构与设计取舍
生成后的典型结构:
| 路径 | 作用 | 是否必留 |
|---|---|---|
functions.php |
主题初始化、注册菜单/侧边栏/脚本 | 必留 |
header.php / footer.php |
头尾结构 | 必留 |
index.php |
兜底模板 | 必留(WordPress 强制) |
single.php / page.php / archive.php / search.php / 404.php |
各场景模板 | 按需 |
sidebar.php |
侧边栏 | 无侧边栏可删 |
comments.php |
评论模板 | 按需 |
template-parts/ |
可复用的内容片段(content.php 等) | 建议留 |
inc/template-tags.php |
自定义模板标签(posted_on 等) | 建议留 |
inc/template-functions.php |
钩子类辅助函数 | 建议留 |
inc/customizer.php |
Customizer 示例 | 按需 |
inc/jetpack.php |
Jetpack 支持声明 | 不用 Jetpack 可删 |
sass/ |
SCSS 源码,分模块 | 必留(或换构建方式) |
js/navigation.js、js/skip-link-focus-fix.js |
菜单与跳转链接脚本 | 建议留 |
_s 的哲学是「少即是多」:它故意不提供任何视觉设计(连 .container 都没有),逼你从零写样式;同时把所有 WordPress 规范动作都做对(wp_body_open、body_class、post_class、the_posts_navigation、escaping 全覆盖),保证你能直接提交官方目录审核。
一个关键细节:所有函数名都以 _s_ 开头,生成时若指定了 slug 会自动替换为你的前缀(如 mimangfei_)。如果手动下载 zip,记得全局替换,否则插件/子主题冲突时很难排查。
保姆级实操
第一步:生成主题骨架
两种方式。官网生成(underscores.me)填主题名与作者即可下载;命令行更可控:
cd wp-content/themes
wp scaffold _s mimangfei \
--theme_name="迷忙飞主题" \
--author="迷忙飞" \
--author_uri="https://mimangfei.com" \
--activate
生成后先确认前缀替换是否成功:
grep -rn "_s_" mimangfei/ | head -20
第二步:精简
cd mimangfei
# 不用 Jetpack
rm inc/jetpack.php
# 站点无侧边栏(企业站常见)
rm sidebar.php
# 不需要自定义 Customizer 示例
rm inc/customizer.php
同时把 functions.php 里对应的 require 删掉:
require get_template_directory() . '/inc/template-tags.php';
require get_template_directory() . '/inc/template-functions.php';
// 已删除:inc/customizer.php、inc/jetpack.php
删 sidebar.php 后,还要去掉 register_sidebar() 的调用和 functions.php 里的 mimangfei_widgets_init,否则后台小工具页面会有空区域。
第三步:补齐主题支持与菜单
if ( ! function_exists( 'mimangfei_setup' ) ) :
function mimangfei_setup() {
load_theme_textdomain( 'mimangfei', get_template_directory() . '/languages' );
add_theme_support( 'automatic-feed-links' );
add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'editor-styles' );
add_theme_support( 'html5', array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'style',
'script',
) );
register_nav_menus( array(
'primary' => esc_html__( '主导航', 'mimangfei' ),
'footer' => esc_html__( '页脚导航', 'mimangfei' ),
) );
}
endif;
add_action( 'after_setup_theme', 'mimangfei_setup' );
set_post_thumbnail_size( 1200, 675, true ) 这类按需补,title-tag 一定不能漏(否则 <title> 不输出,SEO 直接扣分)。
第四步:补齐内容模板
template-parts/content.php 是列表与详情页共用的片段:
<article id="post-<?php the_ID(); ?>" <?php post_class( 'entry' ); ?>>
<header class="entry-header">
<?php the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' ); ?>
<div class="entry-meta">
<?php mimangfei_posted_on(); ?>
</div>
</header>
<?php if ( has_post_thumbnail() ) : ?>
<a class="entry-thumb" href="<?php the_permalink(); ?>">
<?php the_post_thumbnail( 'medium_large', array( 'loading' => 'lazy' ) ); ?>
</a>
<?php endif; ?>
<div class="entry-summary">
<?php the_excerpt(); ?>
</div>
</article>
再根据设计稿补 template-parts/content-single.php(正文页)、template-parts/content-page.php(页面),用 get_template_part( 'template-parts/content', 'single' ) 按需引入。
第五步:接上构建与样式
npm install
npx sass sass/style.scss style.css --watch
sass/ 目录下按模块拆分,建议的改造是加一个 _variables.scss 统一颜色与间距:
// sass/variables-site/_variables-site.scss
$mf-accent: #2563eb;
$mf-text: #1a1a1a;
$mf-muted: #6b7280;
$mf-container: 1200px;
$mf-gap: 1.5rem;
按需求可换成 Tailwind 之类的方案,思路见 Tailwind 与 WordPress 主题结合。
第六步:脚本与样式的加载检查
add_action( 'wp_enqueue_scripts', 'mimangfei_scripts' );
function mimangfei_scripts() {
wp_enqueue_style( 'mimangfei-style', get_stylesheet_uri(), array(), wp_get_theme()->get( 'Version' ) );
wp_enqueue_script(
'mimangfei-navigation',
get_template_directory_uri() . '/js/navigation.js',
array(),
wp_get_theme()->get( 'Version' ),
true
);
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
wp_enqueue_script( 'comment-reply' );
}
}
第七步:补齐 style.css 头部与截图
/*
Theme Name: 迷忙飞主题
Theme URI: https://mimangfei.com/themes/mimangfei
Author: 迷忙飞
Author URI: https://mimangfei.com
Description: 轻量、语义化、面向内容站点的 WordPress 主题。
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: mimangfei
Tags: blog, custom-logo, custom-menu, translation-ready
*/
再加一张 1200×900 的 screenshot.png,后台就能看到主题预览图。
常见踩坑
- 忘了全局替换
_s_前缀:与父主题或插件函数重名,出现「Cannot redeclare」致命错误。 - 删了
sidebar.php却没删get_sidebar():前台报模板文件缺失的致命错误。 load_theme_textdomain写错 text domain:所有__()翻译都不生效,且 Theme Check 会报错。- Sass 编译出来的 style.css 覆盖了头部注释:
sass/style.scss编译时不会自动保留注释,头部注释应写在编译产物的style.css顶部或用/*! */保留注释语法。 wp_body_open()缺失:插件注入内容(如统计代码、AMP 组件)位置错乱。- 案例:某开发者用 underscores.me 生成主题后直接开始写样式,交付时客户在后台「外观 → 菜单」里找不到菜单位置——因为
mimangfei_setup()里只注册了默认的menu-1,而他改了模板里的theme_location却没同步注册。排查方式是比对register_nav_menus与wp_nav_menu( array( 'theme_location' => ... ) )两个字符串是否完全一致。 - 把
index.php删掉:WordPress 要求主题必须有index.php作为兜底,缺失会导致主题无法启用。 npm install后忘了编译:style.css只有头部注释没有任何样式,前台一片空白。
总结
_s 的正确用法是「当成规范参考,而不是最终产品」。交付前的检查点:所有函数名已换成自己的前缀、未使用的 inc/ 文件已删且 require 已同步、title-tag 与 post-thumbnails 已声明、Sass 编译产物里保留了头部注释、screenshot.png 就位、用 Theme Check 跑一遍无 REQUIRED 项(见 Theme Check 自检)。这套流程走完,你的主题在结构上已经和官方目录里的优质主题没有本质差别;若想进一步做视觉统一,可继续参考 theme.json 全局样式 的做法迁移到区块主题。
