{extend}未生效的根本原因是模板引擎未执行继承逻辑,因父模板不存在、路径错误或子模板首行不合规(必须严格第一行且无空格/注释),且名称大小写敏感、需显式匹配。

为什么{extend}写了但页面没套布局
根本原因是模板引擎压根没执行继承逻辑,不是语法写错,而是前置条件不满足。ThinkPHP 的 {extend} 不依赖 layout_on 配置,但它要求父模板存在、路径正确、且子模板首行严格合规。
-
{extend}必须出现在子模板**第一行**,前面不能有空格、空行、PHP 注释或任何字符 -
name值是相对于view_path的路径,不带后缀:比如view/public/base.html对应{extend name="public/base"} - 父模板里必须定义了同名
{block name="xxx"},否则子模板的{block}会被静默忽略(无报错) - TP6 起部分驱动要求后缀显式写出,如
{extend name="public/base.html"},旧写法可能失效
子模板写了{block}但内容没显示或被清空
这是对 {block} 行为的典型误读:它不是“占位填充”,而是“完全替换”。父模板中该区块的所有内容(含 HTML、变量、{include})都会被子模板同名 {block} 内容覆盖。
- 如果父模板
{block name="content"}<p>默认内容</p>{/block},子模板只写{block name="content"}新内容{/block},那默认内容就彻底消失 - 想保留父内容并追加,必须在父模板中用
{__BLOCK__}显式留位,例如:{block name="script"}{__BLOCK__}<script>loadMore();</script>{/block} - 大小写和下划线敏感:
content≠Content≠content_,名称不一致即配对失败 - 子模板中重复定义同一
name的{block},仅最后一个生效
{include} 和 {extend} 能不能混着用
能,但必须清楚它们的执行阶段和作用域——混用不当会导致区块不可覆盖、资源重复加载或结构错乱。
-
{include}是运行时引入,发生在模板编译之后;{extend}是编译期处理,子模板内容先“注入”父模板对应{block},再整体编译 - 父模板里
{include file="public/header"}中的代码,对子模板的{block}完全不可见,子模板无法覆盖 header 里的任何部分 - 不要试图用继承去“改”已
{include}进来的内容;公共片段(导航、页脚)应统一抽成独立模板,在父/子模板中各自{include} - 若需动态控制 header 内容,应把 header 拆成带
{block}的子布局,再让 base 模板继承它
开启 layout_on 后还是不走布局
layout_on 配置只影响 fetch() 等底层调用,对控制器中常见的 return $this->fetch(); 默认无效。它和 {extend} 是两套机制,别指望开了就能自动套 layout.html。
立即学习“PHP免费学习笔记(深入)”;
- 启用
layout_on后,必须确保layout_name指向真实存在的文件,比如'layout_name' => 'layout'对应view/layout.html - 修改配置后务必清空
runtime/view/下的缓存文件,否则旧编译结果会掩盖配置变更 - 控制器中更可靠的方式是显式调用:
$this->view->engine->layout('public/layout'); - 想临时关闭某次渲染的布局,可在模板首行加
{__NOLAYOUT__}(注意是两个下划线)
最易被忽略的是:{extend} 和 {block} 的配对是纯字符串匹配,没有 fallback、不报错、不警告。一旦名称拼错或路径不对,结果就是静默失效——页面看起来“正常”,但结构已经脱离预期。调试时直接查看最终输出的 HTML 源码,比翻日志更快定位问题根源。



















