thinkphp 8.0 的 layout 和 {extend} 是互斥机制:layout_on 仅对 fetch() 生效,通过 content 替换;{extend} 是编译期继承,需首行书写、路径正确、父模板含 {block},且不支持嵌套继承与 include 内的 block 覆盖。

ThinkPHP 8.0 的模板布局(layout)和模板继承({extend})是两套独立机制,不能混用;绝大多数“布局不生效”问题,本质是误把二者当同一功能在用。
为什么 layout_on 开启了但页面没套布局?
因为 layout_on 只对 $this->fetch() 生效,且它走的是“布局替换”逻辑:先渲染子模板 → 得到 HTML 字符串 → 再用 {__CONTENT__} 替换进布局文件。它不解析 {block},也不支持区块覆盖。
- 必须确保配置中
'layout_on' => true和'layout_name' => 'layout'同时存在(layout_name默认值是layout,但若你放的是view/public/layout.html,就得写成'layout_name' => 'public/layout') - 布局文件里必须包含且仅包含一个
{__CONTENT__}占位符,多一个空格、换行或错写成{__content__}都会导致子模板内容被丢弃 - 子模板开头**不能**写
{extend},否则会跳过 layout 流程——TP8 中{extend}和layout_on是互斥的 - 想临时关闭某页布局?在子模板第一行写
{__NOLAYOUT__}(注意是两个下划线)
为什么写了 {extend name="layout"} 却完全没加载父模板?
因为 {extend} 是编译期指令,路径错误或语法位置不对,引擎会静默跳过,不报错也不渲染父模板。
-
{extend}必须出现在子模板**第一行、首字符位置**,前面不能有空格、空行、PHP 注释(//或/* */)甚至 UTF-8 BOM -
name值是相对view_path的路径,不带后缀:{extend name="public/layout"}对应app/view/public/layout.html,写成public/layout.html就失败 - 父模板(如
layout.html)里**必须定义至少一个{block name="xxx"}**,否则子模板中同名{block}会被忽略(不是报错,是直接丢弃) - 检查运行时日志,搜
think\template\driver\File::read,如果没出现layout.html的读取记录,说明路径或文件名已出错
{block} 为什么只显示子模板内容、父模板默认文字没了?
因为 {block} 行为是「完全替换」,不是「追加」或「装饰」。父模板里 {block name="title"}首页{/block},子模板写 {block name="title"}后台管理{/block},结果就是只显示“后台管理”。
- 要保留父内容并追加/前置/包裹,必须在父模板中显式使用
{__BLOCK__}占位符:{block name="title"}{__BLOCK__} | 后台管理{/block} -
{__BLOCK__}只能在父模板的{block}内部使用,子模板里写它无效 - 大小写敏感:
name="Title"和name="title"是两个不同区块,不会匹配 - 不支持嵌套继承链:A 继承 B,B 继承 C,A 无法直接覆盖 C 中的 block,除非 B 显式声明并透传(例如 B 中写
{block name="script"}{__BLOCK__}{/block})
该用 layout_on 还是 {extend}?
选哪个取决于你要解决什么问题:需要全站统一套壳(header + footer + content 区域),用 layout_on;需要多级结构复用(比如 admin/base → admin/list → admin/edit),必须用 {extend}。
最容易被忽略的一点:TP8 的 {include file="xxx"} 不参与继承链——你在父模板 {include} 进来的文件里写的 {block},子模板根本覆盖不了。这类复用逻辑,得拆成独立模板,由父子双方各自 {include},而不是指望继承去“改”它。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











