{extend}必须严格位于子模板第一行首字符位置,否则继承静默失效;常见原因包括BOM头、空格、空行、PHP注释等前置字符,导致引擎跳过继承逻辑,仅渲染子模板内容。

{extend} 必须放在子模板第一行,否则整个继承逻辑失效,页面只渲染子模板原始内容。
为什么 {extend} 写了但父模板完全不显示
最常见原因是子模板开头存在不可见字符:BOM头、空格、空行、PHP注释或 <?php ?> 标签。ThinkPHP 模板引擎在解析时一旦发现 {extend} 不在绝对首行,就直接跳过继承,不报错也不警告。
- 用编辑器开启“显示所有字符”功能,确认
{extend name="layout" /}是文件第一行第一个字符 -
name值是相对于view_path的路径,比如配置中'view_dir_name' => 'view',那name="public/layout"对应的就是app/view/public/layout.html(TP6 起必须带.html后缀) - 父模板里至少得有一个
{block name="xxx"},否则子模板所有{block}都被静默忽略 - 大小写和下划线严格区分:
name="Header"≠name="header",TP 不做任何转换
子模板 {block} 覆盖失败的典型表现
写了 {block name="title"}后台管理{/block},但页面仍显示父模板里的“默认标题”——这不是缓存问题,而是覆盖规则被误读。
-
{block}是“替换”,不是“追加”。父模板{block name="title"}首页{/block}+ 子模板同名 block → 最终只显示“后台管理” - 想保留父内容再叠加?父模板得写成
{block name="title"}{__BLOCK__} - 后台系统{/block},其中{__BLOCK__}是占位符,表示插入子模板该 block 的内容 - 不能在同一个子模板里重复定义同名
{block},后出现的那个生效,前面的被丢弃 - 父模板若该
{block}为空,子模板内容会正常显示;若父模板已有内容且没写{__BLOCK__},子内容就彻底覆盖掉
{include} 和 {extend} 混用时的加载时机陷阱
很多人在父模板里 {include file="common/header"},然后指望子模板去覆盖 header 里的某个 {block},结果完全无效。
立即学习“PHP免费学习笔记(深入)”;
-
{include}是运行时引入,发生在模板编译完成之后;{extend}是编译期注入,子模板内容先被“塞进”父模板对应{block},再整体编译 - 所以父模板中
{include}引入的文件,其内部的{block}对子模板不可见,也无法被覆盖 - 正确做法:把需要定制的部分(如标题、JS 加载位置)直接抽到基础布局模板(
layout.html)的{block}里;公共片段(导航、页脚)各自{include},不依赖继承链 - 真要多层定制,可让
header.html本身也支持{block},再让layout.html继承它,形成多层继承
前端框架整合时的模板边界意识
在 ThinkPHP 视图中集成 Vue/React 时,别试图用 {block} 去动态控制 JS 资源加载顺序或组件挂载点——这容易和前端框架生命周期冲突。
- 静态资源(CSS/JS)建议统一由父模板
{block name="script"}管理,子模板只负责填充业务相关脚本,避免重复加载 - Vue 组件初始化代码不要写在
{block name="content"}里,而应放在独立的{block name="vue_init"}中,并确保执行时机在 DOM 渲染后 - TP6 默认关闭
layout_on,但它跟{extend}无关;有人开了layout_on还以为就能用继承,其实只是启用了旧式{__CONTENT__}布局机制 - 前后端分离项目中,TP 更适合作为纯 API 层;若坚持视图层混合渲染,务必明确 HTML 结构归属权——TP 控制骨架,前端框架控制局部动态区域
真正卡住人的从来不是语法,而是对 {extend} 编译期行为和 {include} 运行时行为的混淆。一旦开始调试继承失效,第一反应不该是清缓存,而是打开十六进制编辑器看子模板开头三个字节是不是 7B 65 78(即 {ex)。



















