在PHP框架中利用模板继承构建可维护的前端视图结构
在PHP框架中构建前端视图时,比较容易维护的做法不是让每个页面重复写完整的
<html>、导航栏、侧边栏和页脚,而是把这些公共结构集中到一个基础布局中,
再让具体页面通过模板继承只填写自己真正不同的内容。配合局部模板和组件,可以把“页面骨架”“页面内容”和“重复模块”
分成不同层次,后续修改导航、资源引用或整体布局时不必逐页处理。
一、模板继承解决的核心问题是什么
假设一个后台系统包含用户列表、订单列表、文章管理和系统设置四个页面。它们可能都有相同的顶部导航、 左侧菜单、CSS文件、JavaScript文件和页脚,真正变化的通常只是页面标题与中间内容。
如果每个页面都保存一份完整HTML结构,就会产生大量重复代码。导航增加一个菜单时需要修改多个文件, CSS或JavaScript引用发生变化时也容易遗漏某些页面。
模板继承的思路是把页面分成几个层次:
- 基础布局:负责HTML骨架、公共资源、导航、侧栏和页脚。
- 内容区块:由不同页面覆盖,例如页面标题、主体内容和额外脚本。
- 局部模板或组件:负责表格、分页、提示框、表单字段等重复出现的小结构。
二、先建立一个稳定的基础布局
模板继承的第一步不是马上拆很多组件,而是找到多个页面真正共有的部分。一般可以从HTML文档结构、 CSS资源、顶部导航、侧栏、主体容器和JavaScript资源开始。
不同PHP框架的模板语法不完全相同,但通常都存在类似“继承布局”“定义区块”“输出区块”的能力。
下面使用接近常见PHP模板引擎的伪代码说明结构,具体项目中可以换成框架实际提供的
extends、section、block、yield 等指令。
例如建立一个基础布局文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>@yield('title', '管理后台')</title>
<link rel="stylesheet" href="https://www.ipython.cn/assets/app.css">
@yield('styles')
</head>
<body>
@include('partials.header')
<div class="app-layout">
@include('partials.sidebar')
<main class="main-content">
@yield('content')
</main>
</div>
@include('partials.footer')
<script src="https://www.ipython.cn/assets/app.js"></script>
@yield('scripts')
</body>
</html>
这里真正值得注意的不是指令名称,而是结构划分:
header、sidebar 和 footer 属于公共区域,
content 是每个页面最主要的变化区域,而 styles 与
scripts 则为少数页面留下扩展入口。
三、让业务页面只描述自己的内容
基础布局建立后,用户列表页不再负责输出完整HTML文档,而是继承公共布局并填写自己的页面标题和主体内容。
@extends('layouts.admin')
@section('title', '用户管理')
@section('content')
<div class="page-header">
<h1>用户管理</h1>
<a href="/users/create" class="button">新增用户</a>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
<a href="/users/{{ $user->id }}/edit">编辑</a>
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection
页面文件现在只包含“这个页面与其他页面有什么不同”。如果以后整个后台的侧栏宽度、导航结构或公共脚本发生变化, 主要修改基础布局及对应局部模板即可,业务页面不需要重复调整。
四、不要把模板继承和局部模板混成一件事
模板继承和局部模板都可以减少重复代码,但解决的问题不同。理解这一点之后,视图目录通常会清楚很多。
模板继承处理的是纵向层级关系。例如后台所有页面都建立在同一个管理后台骨架上, 用户页面继承后台布局,订单页面也继承后台布局。
局部模板处理的是横向复用关系。例如用户页和订单页都需要分页条, 这时分页并不适合成为整个页面的父模板,而更适合作为一个可以被不同页面包含的独立模块。
| 需求 | 更适合的方式 | 原因 |
|---|---|---|
| 多个后台页面共享导航、侧栏和页脚 | 模板继承 | 这些内容构成稳定的页面骨架,存在明确父子关系。 |
| 多个页面都需要同一个分页结构 | 局部模板或组件 | 分页只是页面中的一个小模块,不决定整个页面结构。 |
| 普通后台页与登录页结构完全不同 | 分别建立布局 | 强行共用同一个布局会产生大量条件判断。 |
| 某个页面需要额外加载编辑器脚本 | 扩展脚本区块 | 公共脚本仍由父布局管理,只为特殊页面增加附加资源。 |
五、为什么简单 include 拼装和模板继承维护起来不同
PHP项目也可以通过多个 include 把头部、菜单和页脚拼起来。对于页面数量很少、
结构也很简单的项目,这种方式并非不能使用。
差别主要出现在页面结构开始复杂以后。简单的 include 更像是“当前页面主动把几个文件拼进来”, 模板继承则是“父布局规定整体结构,子页面只提供指定内容”。后者的页面结构约束通常更明确。
两者不是必须二选一。一个常见而清晰的组合就是: 用模板继承控制页面级结构,用 include 或组件复用页面中的小模块。
六、为不同类型的页面建立合适的布局层级
模板继承并不意味着整个项目只能有一个基础布局。一个网站经常同时存在前台、管理后台和登录认证页面, 它们的结构可能明显不同。
例如可以采用这样的目录:
views/
├── layouts/
│ ├── app.php
│ ├── admin.php
│ └── auth.php
│
├── partials/
│ ├── header.php
│ ├── footer.php
│ ├── sidebar.php
│ └── pagination.php
│
├── users/
│ ├── index.php
│ ├── create.php
│ └── edit.php
│
├── orders/
│ ├── index.php
│ └── show.php
│
└── auth/
├── login.php
└── register.php
这里的 app.php 可以作为网站前台布局,admin.php 负责管理后台,
auth.php 则用于登录、注册等不需要后台侧栏的页面。
这样比在一个超级布局里不断写
if ($isLoginPage)、if ($showSidebar)、
if ($isAdmin) 更容易维护。条件判断不是完全不能出现,而是不应该用大量条件去掩盖实际上已经不同的页面结构。
七、只在需要时给页面增加专属CSS和JavaScript
一个常见问题是:公共布局已经统一加载CSS和JavaScript,但某个编辑页面还需要富文本编辑器或图表库, 难道要把这些资源加载到所有页面吗?
更合适的方式是在基础布局中保留额外资源区块。
<head>
<link rel="stylesheet" href="https://www.ipython.cn/assets/app.css">
@yield('styles')
</head>
...
<script src="https://www.ipython.cn/assets/app.js"></script>
@yield('scripts')
需要额外脚本的页面再单独填写:
@section('scripts')
<script src="https://www.ipython.cn/assets/editor.js"></script>
<script>
initEditor('#content');
</script>
@endsection
普通页面不会因此加载 editor.js,而编辑页面仍然沿用同一个基础布局。
这就是模板区块比较合适的使用场景:整体结构保持一致,但允许少量、明确的页面差异。
八、模板中尽量不要塞入业务逻辑
模板继承解决的是视图结构问题,并不意味着所有代码都应该搬进模板。控制器、服务层和模板之间仍然需要保持职责边界。
例如用户状态文字最好在进入视图之前已经形成容易使用的数据,而不是在模板里不断处理复杂判断:
<!-- 不建议让模板承担过多业务判断 -->
@if ($user->status === 1 && $user->expired_at > time() && ...)
...
@endif
模板中的条件判断适合控制“是否显示某个区域”“当前列表是否为空”之类的展示逻辑。 如果判断已经涉及复杂业务规则,应尽量在控制器、服务或专门的视图数据转换层中处理。
这样做与模板继承的目标是一致的:让视图文件尽可能只回答“页面应该怎样展示”,而不是同时负责大量业务计算。
九、常见的几个维护问题
1. 基础布局越来越大
如果导航、用户菜单、消息中心、侧栏和页脚全部直接堆在布局文件中,布局本身仍然会变得难读。 可以继续把稳定的独立区域拆成局部模板:
@include('partials.header')
@include('partials.sidebar')
@include('partials.footer')
基础布局负责确定这些区域出现在哪里,而局部模板负责各自区域内部的HTML。
2. 页面继承层级太深
理论上可以设计多层继承,例如基础布局 → 后台布局 → 内容管理布局 → 文章编辑布局。 但层级过深后,开发者很难快速判断某段HTML到底由哪个模板输出。
如果只是为了复用一个小区域,通常更适合拆成组件或局部模板,而不是继续增加继承层级。
3. 为每个小差异建立区块
一个布局如果出现几十个可覆盖区块,意味着父布局已经很难定义稳定结构。 比较合适的区块通常是标题、主要内容、头部扩展资源、底部扩展脚本,以及少数确实需要覆盖的大区域。
4. 把所有页面强行放进同一个布局
登录页、打印页、错误页和后台工作台可能有完全不同的页面结构。如果为了共享一个父模板而加入大量条件, 建立第二个布局往往会更直观。
十、如何判断当前视图结构是否已经比较合理
完成模板继承改造后,可以从实际维护动作反过来检查结构,而不是只看目录是否整齐。
- 修改顶部导航时,是否只需要修改一个主要位置?
- 新增普通后台页面时,是否只需要编写这个页面自己的正文?
- 修改公共CSS或JavaScript入口时,是否不需要逐个页面修改?
- 重复出现的分页、提示框或表单模块,是否已经形成独立可复用结构?
- 登录页等特殊页面是否能够使用自己的布局,而不是依赖大量条件判断?
- 阅读一个业务页面时,能否很快看出它真正负责什么内容?
十一、一个完整的视图组织思路
把前面的做法组合起来,一个比较清晰的PHP框架视图结构可以这样理解:
- 先识别多个页面真正共有的HTML骨架。
- 把公共骨架放入基础布局,并预留少量明确的内容区块。
- 业务页面继承布局,只输出页面标题、正文和必要的专属资源。
- 分页、提示框、表单字段等重复模块再拆成局部模板或组件。
- 前台、后台、登录等结构差异较大的页面使用不同布局。
- 避免为了复用少量HTML建立过深的继承关系。
- 复杂业务判断放到控制器、服务或数据转换层,模板主要负责展示。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













