本文约 5,198 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:重做成本太高,不迁又眼看着落后
很多运行三五年以上的站点处在一个尴尬位置:老式主题 + 一堆 shortcode + 若干自定义模板文件,想迁移到区块主题,但页面上三四十个定制页面、几十个 PDF、一堆自定义的 meta,重做一次报价可能比当初建站还高。
好消息是 WordPress 的区块体系是可以共存的。核心设计上做了一个关键让步:只要主题目录里存在 templates/index.html,站点编辑器就会被启用;但这套 HTML 模板和你原先的 PHP 模板并不冲突——前者优先,后者兜底。这就为渐进迁移提供了技术基础。
本文的路线就是把一次性的大爆炸式重构,拆解成四个可以分别验证、随时回滚的阶段。每个阶段都有明确的验收点和回滚方案。执行前请先通读区块主题与传统主题的核心区别,理解两类主题在模板来源上的差别。
原理分析:为什么渐进迁移是可行的
核心的兼容机制有三条:
- 模板优先级:区块主题目录里存在
templates/*.html时被优先使用;但 WordPress 的模板层级依然完整。你可以只准备一个templates/index.html,让其余模板暂时仍由 PHP 承担(反过来不行:一旦采用 block theme 体系,模板层级会先找 block templates)。 - theme.json 向下兼容:
theme.json里声明的调色板、字号会影响区块编辑器,其余部分照旧。可以先只迁移“调色板/字号/间距”,不动布局。 - 短代码与区块可共存:Gutenberg 提供
core/shortcode区块,旧短代码可以在新编辑器里继续用短期过渡。
基于这三条,迁移顺序就清楚了:先让它共存 → 再逐个替模板 → 然后把 CSS 迁到 theme.json → 最后把遗留的 shortcode/meta 做成区块。
保姆级实操
阶段一:准备(只做安全动作,不改前台)
# 1) 完整备份:文件 + 数据库
wp db export migration-backup.sql
tar -czf themes-backup.tar.gz wp-content/themes/
# 2) 记录迁移前的基线(后面每个阶段用同样的方式复测)
curl -s -o /dev/null -w "TTFB=%{time_starttransfer} Total=%{time_total}\n" https://example.com/
wp eval "echo wp_get_theme()->get('Version'); echo PHP_EOL;"
模板层面的取舍背景可先看区块主题(FSE)与传统主题的区别。同时在 staging 环境克隆一份用于后续验证 —— 所有改动只在 staging 上做。
阶段二:启用共存 —— 引入 theme.json 的“设置部分”
先只写 settings,让编辑器认识你的品牌色与字号:
{
"version": 2,
"settings": {
"appearanceTools": true,
"color": {
"palette": [
{ "slug": "brand", "color": "#12467b", "name": "品牌蓝" },
{ "slug": "accent", "color": "#e2571f", "name": "强调橙" }
]
},
"typography": { "fluid": true },
"layout": { "contentSize": "760px", "wideSize": "1180px" }
}
}
上线后验证两点:新文章在区块编辑器里能看到品牌蓝;旧内容外观无变化。
阶段三:逐个迁移模板(从风险最低的开始)
推荐的迁移顺序:404 → 搜索结果 → 归档 → 单篇 → 首页。理由很清楚:404/搜索页结构最简单,即使出问题影响面也最小。
mkdir -p wp-content/themes/当前主题/templates wp-content/themes/当前主题/parts
templates/404.html 的最小实现:
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">页面没找到</h1><!-- /wp:heading -->
<!-- wp:paragraph --><p>你要访问的内容可能已被移动,试试搜索。</p><!-- /wp:paragraph -->
<!-- wp:search {"label":"搜索","showLabel":false,"width":50} /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
每迁移一个模板就验证一次:
# 确认核心已识别到模板(会对出错的模板给出提示)
wp eval "print_r( wp_get_theme()->get_block_template_file_names() ?? array() );"
# 检查数据库中由站点编辑器生成的模板版本
wp post list --post_type=wp_template --fields=ID,post_name,post_status
# 前台实际请求 404 页面看结果
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/not-exist-page/
回滚方式(任一模板出问题):把 templates/ 下对应文件移走,或把该 wp_template 记录置为 draft。
wp post update 模板ID --post_status=draft
阶段四:把遗留资产做成区块/图案
对仍在用的短代码,先做一个“短代码区块”桥接层:
<?php
// functions.php —— 把遗留短代码封装成区块,便于在站点编辑器里使用
add_action( 'init', function () {
register_block_type( 'mangfei/legacy-callout', array(
'api_version' => 3,
'editor_script' => 'mangfei-blocks',
'render_callback' => function ( $attrs ) {
return do_shortcode( '[legacy_callout]' );
},
'attributes' => array( 'align' => array( 'type' => 'string' ) ),
) );
// 把常用组合注册为图案,方便运营自助使用
register_block_pattern_category( 'mangfei-library', array( 'label' => '站点图案库' ) );
} );
随后逐步把 do_shortcode 的实现替换成真正的区块渲染,最后一并卸载短代码实现。
阶段五:收尾清理
# 找出正文中还残留的老短代码
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%[legacy_%';"
# 确认还有多少旧模板文件仍被使用
grep -rl "get_header\|get_footer" wp-content/themes/当前主题/*.php | head
# 用 WP-CLI 检查站点的/site-editor 模板健康状况
wp eval "echo count( get_block_templates( array(), 'wp_template' ) ); echo ' 个模板'; echo PHP_EOL;"
常见踩坑
- 一次性把所有模板都换成 HTML。没有分阶段的验证,出问题无法定位是哪一层。务必一个模板一个 PR。
- 忘了 parts/header 与 parts/footer 是必须的。只放
templates/index.html却没提供 header/footer part,页面会缺页头页脚。 - 直接把旧 CSS 全量拷进 theme.json 的 styles。theme.json 的 styles 只能表达有限的一部分视觉,复杂选择器仍需 CSS 文件。
- 忽略 Role/capability。站点编辑器需要
edit_theme_options能力,迁移后要给运营角色评估是否放开。 - 在多站点/多种语言站点跳过逐语言检查。每语言的菜单和模板索引都不一样,需逐语言验证。
- 没备份就改 theme.json。theme.json 解析失败时,全站的 preset 会静默回退到核心默认值,回放很快。
- 案例:某站点在周五下午一次性把 6 个模板转成区块主题,没意识到旧模板里有
WP_Query做的“按 meta 排序的案例列表”。新index.html里的core/query区块无法表达这个排序,导致首页案例顺序混乱。周一花了半天才发现。教训:每迁移一个模板之前,先把这个模板里所有 PHP 逻辑列出来;凡是涉及自定义查询/meta/权限的部分,都不是纯 HTML 模板能表达的,必须做 custom block。
总结
迁移路线的四个验收点,逐条确认即可:theme.json settings 上线后旧内容不受影响 → 每个迁移的模板单独验证并保留回滚(改 wp_template status)→ 遗留短代码完成区块桥接 → 全站清洁后残余 PHP 模板文件逐步下线。
判断是否可以开始:只有当某个模板里没有自定义查询、没有 meta 依赖、没有权限判断时,它才适合用纯 HTML 模板替换。符合这个标准的通常是 404/搜索/静态页面;不符合的(归档排序、会员可见性)应该先写成自定义区块,再放进模板。按这个原则排队推进,风险可控在单个模板内。
