你的模板文件八成是被数据库里的一份副本压过去了。只要你在站点编辑器里打开某个模板并按下保存,WordPress 就会写入一条 wp_template 记录,此后输出的都是这条记录,而不是主题文件夹里的 .html 文件——所以你之后对文件所做的每一次修改,在前台都看不见。清掉这条记录就是解决办法,也是在你去动模板层级、文件名或缓存之前最该先查的一件事。
先确认数据库副本存在
进入 外观 → 编辑器 → 模板。凡是 WordPress 正在从数据库输出的模板,都会在列表里被标记出来——根据你的 WordPress 版本,标签可能写作“已定制”,或者这些模板会出现在一个“已定制”的筛选项下。这个标记就是全部答案。它在,你的文件就没被用上。
同样的事情也可以从命令行查,更快,而且在站点编辑器本身抽风时照样能用:
wp post list --post_type=wp_template --fields=ID,post_name,post_status,post_title
wp post list --post_type=wp_template_part --fields=ID,post_name,post_status,post_title
每一行都是一个数据库层面的覆盖。post_name 对应模板的别名,所以一行名为 single 的记录,压住的就是 templates/single.html。
有个细节容易把人绕进去:这些记录是通过一个分类法术语跟主题关联的,而不是通过路径。如果你把主题文件夹改了名,保存的模板还留在数据库里,但不再匹配当前主题,站点会突然回落到文件。这看起来像“我的定制全没了”,而不是“我的文件被忽略了”——同一个机制,相反的症状。
清掉它
在站点编辑器里,打开模板的选项菜单(旁边那三个点),选择 清除定制——有些版本把它写作 重置。这会删掉数据库记录,控制权回到磁盘上的文件。
如果菜单里给的是 删除 而不是清除定制或重置,说明这个模板只存在于数据库里,背后没有文件。删掉它就等于把这个模板整个移除,WordPress 会沿着层级往下回落。
用 WP-CLI 的话,拿上面列表里的 ID:
wp post delete 482 --force
这里要对自己诚实一点,认清代价:清除定制会永久丢掉你在站点编辑器里为那个模板做过的所有东西,而且无法撤销。如果站点编辑器里的版本确实有实打实的工作量,先把区块标记复制出来——在编辑器里打开模板,切到代码视图,把内容粘进你的 .html 文件。这样文件就成了唯一的事实来源,也什么都没丢。
WordPress 是怎么挑模板的
区块主题用的层级逻辑和经典主题一样,只是把主题根目录下的 .php 文件换成了 templates/ 里的 .html 文件。WordPress 从最具体走到最不具体,用上第一个存在的文件。
| 当前访问 | 依次检查的顺序 |
|---|---|
| 一篇文章 | single-post-{slug}.html → single-post.html → single.html → singular.html → index.html |
| 一个页面 | page-{slug}.html → page-{id}.html → page.html → singular.html → index.html |
| 一个分类归档 | category-{slug}.html → category-{id}.html → category.html → archive.html → index.html |
| 一种自定义文章类型 | single-{post-type}.html → single.html → singular.html → index.html |
| 404 页面 | 404.html → index.html |
有两样东西会彻底越过这套顺序。第一是上面说的数据库记录。第二是直接指派给某篇文章或页面的模板——打开文章编辑器,看侧边栏里的“模板”面板,确认那里有没有选中某个特定模板。显式指派永远赢过层级,而且你不专门去找就发现不了。
按别名命名的模板有一种很值得记住的脆弱性。page-about.html 只在页面别名是 about 时才匹配。把页面改名成 about-us,模板就不再生效,没有任何警告,也没有报错。
模板与模板部件
模板渲染一整个视图,由层级来挑选。模板部件是片段——页头、页脚、侧边栏——由模板引入。部件放在 parts/,模板放在 templates/,在数据库里也是分开存的,部件存为 wp_template_part 记录。
常见的困惑是:你改了页头,结果在你没打算动的页面上也变了。这是正确行为。一个部件被所有引入它的模板共享。如果你想让某个模板用不一样的页头,就得再做一个部件文件,并让那个模板去引用它——改共享的那个,永远换不来按模板区分的效果。
反过来的情况也有。你把一整套页面布局塞进 parts/,然后纳闷为什么层级从来不选它。部件根本不在层级的候选范围里,任何请求都不会直接路由到它们。
文件命名和位置
在往更玄乎的方向猜之前,先核对这几条:
- 文件夹叫
templates/,复数,位于主题根目录。不是template/,也不是block-templates/。block-templates/是区块主题进入核心之前、Gutenberg 插件时代的正确名称,现在还有一大堆教程这么写——它在当前版本的 WordPress 里不管用。 - 模板部件放
parts/,不是template-parts/(那是经典主题放 PHP 片段的惯例)。 - 扩展名是
.html。templates/里的.php文件会被忽略。 - 文件名全小写,用连字符。
single-Post.html或single_post.html都不会匹配。在 macOS 这种不区分大小写的文件系统上,本地跑得好好的,一上 Linux 服务器就坏——典型的“在我机器上是好的”。 templates/index.html必须存在。没有它,WordPress 不会把这个主题归类为区块主题,站点编辑器甚至可能压根不出现。- 如果你在子主题里工作,子主题的模板优先于父主题。子主题里遗留的一个旧文件,会悄悄压过你正在改的那个父主题文件。
想让自定义模板能在文章编辑器里被选到,还得在 theme.json 的 customTemplates 下声明它,写上文件的基础名、一个给人看的标题,以及它适用的文章类型。没有这一条,文件存在,但永远不会出现在“模板”面板里。
缓存——放到最后查
缓存确实是真实存在的原因,但它的可能性最低,一上来就往这儿想,是白白搭进去一下午的典型方式。
清掉你的页面缓存;如果你跑着 Redis 或 Memcached 这类持久化对象缓存,也一并清掉——模板解析结果可能被存在那里。然后绕过浏览器缓存重新加载页面。
没用的操作是清 OPcache。区块模板是 .html 文件,从来不会被 PHP 编译,OPcache 跟它们毫无关系。如果你曾经觉得清 OPcache 把这个问题修好了,那一定是同一时间还有别的东西变了。
还是不行?
如果数据库里没有记录、文件名没写错、文章上也没有显式指派模板、缓存也都清过了,那就去看插件。任何会过滤模板解析的东西——页面构建器、会员插件、多语言插件、在线课程插件——都可能在请求的后期塞进自己的模板。换成一个默认区块主题、停用全部插件测一遍,然后一个一个重新启用。
还有一种情况:模板确实生效了,但渲染出来是空的,那是另一个问题。检查文件里的区块标记是否合法,因为一个写坏的区块注释会让编辑器悄悄剥掉内容,而不是抛出错误。
FAQ
常见问题
WordPress 的区块模板为什么不生效?
通常是因为数据库里保存的一份副本盖过了文件。只要你在站点编辑器里编辑模板并点了保存,WordPress 就会创建一条 wp_template 记录,之后输出的是这条记录,而不是主题文件夹里的 .html 文件。从那一刻起,你对文件做的每一次修改在前台都是看不见的。
怎么让 WordPress 用我的主题文件,而不是站点编辑器里的版本?
打开“外观 → 编辑器 → 模板”,找到那个模板,在它的选项菜单里选择清除定制或重置。这会删掉数据库记录,把控制权交还给磁盘上的文件。如果菜单里给的是“删除”而不是“重置”,说明这个模板只存在于数据库里,背后并没有对应的文件。
WordPress 主题里的区块模板文件该放在哪?
完整模板放在主题根目录下的 templates 文件夹里,可复用的片段放在 parts 文件夹里。两者都用 .html 扩展名、全小写文件名,并且用连字符而不是下划线。另外区块主题必须有 templates/index.html,否则 WordPress 根本不会把这个主题当成区块主题。
模板和模板部件有什么区别?
模板渲染的是一个完整页面,由模板层级根据当前访问的内容来挑选。模板部件则是页头、页脚这样的片段,由模板引入使用。改动页头这个部件,会影响所有引入它的模板,这也是为什么改部件常常看起来像是改错了地方。
缓存会导致区块模板不更新吗?
有时会,但它是可能性最低的原因,应该放到最后再查。页面缓存和持久化对象缓存都可能留着过期的标记,所以下结论之前把两者都清一遍。清 OPcache 在这里没有任何用,因为区块模板是 .html 文件,本来就从来不会被 PHP 编译。
为什么我按页面别名命名的模板突然不管用了?
按别名匹配的模板是在请求时拿当前文章的别名来比对的。改页面名称会改掉它的别名,所以一旦页面变成 about-us,page-about.html 就悄无声息地不再匹配了。分类和标签模板也一样。要么把文件改成新别名,要么在文章编辑器侧边栏里显式指定模板。