在 ThinkPHP 里,模板引擎本质上解决的是“控制器如何把数据交给视图并完成页面渲染”的问题。真正落地时,关键不只是会写模板文件,还要搞清楚视图配置、fetch 调用、模板继承和标签输出分别承担什么职责。下面按开发顺序拆开说明,读完后你可以判断一个页面应该从配置入手,还是从模板结构和数据传递方式入手。
先把视图层配置好
要让 ThinkPHP 正常识别模板文件,第一步是配置视图层参数。通常在 config.php 或 app.php 中完成,至少要明确模板目录、模板后缀,以及当前使用的模板引擎类型。
这一步的意义很直接:框架需要知道去哪里找模板、按什么后缀解析,以及由哪套引擎规则来处理模板语法。
// config.php 或 app.php
return [
// 视图配置
'view' => [
'template_path' => __DIR__ . '/../view', // 模板文件目录
'template_suffix' => 'html', // 模板文件后缀
'template_engine' => 'think', // 使用的模板引擎
],
];
如果这里配置错误,后面的模板创建和渲染调用即使语法没问题,也可能出现找不到模板、页面不输出或渲染异常等情况。
模板文件应该怎么创建
视图层参数确定之后,就可以在对应目录下创建模板文件。示例中模板目录是 application/view,文件后缀使用 .html,因此可以先准备一个最基础的 index.html。
这个阶段建议先从最小可运行页面开始,只保留静态结构和一个变量输出位,先确认模板文件已经被正确识别。
<!-- application/view/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>欢迎来到首页</h1>
<p>{$message}</p>
</body>
</html>
这里的 {$message} 就是模板变量占位符,它本身不会生成内容,必须由控制器在渲染时传入对应数据。
控制器如何渲染模板并传值
在 ThinkPHP 中,最常见的做法是由控制器通过 fetch 方法渲染模板,并把页面所需数据一并传入。这样可以把业务处理放在控制器,把展示逻辑留在模板中。

// application/controller/Index.php
namespace appcontroller;
use thinkController;
class Index extends Controller
{
public function index()
{
$data = [
'message' => 'Hello, ThinkPHP!',
];
return $this->fetch('index', $data);
}
}
这里可以直接看出一条完整链路:
- 控制器方法准备数据;
- 通过
$this->fetch('index', $data)指定模板; - 模板中的
{$message}接收并输出传入值。
如果你只是要让一个页面先显示出来,这通常已经够用;而一旦页面结构开始变复杂,就需要进一步引入模板复用能力。
页面结构重复时,用模板继承复用布局
当多个页面都包含相同的页头、主体区域和页脚时,继续重复写整套 HTML 会很快变得难维护。ThinkPHP 提供的模板继承机制,就是用来处理这一类公共结构复用问题。

基础模板怎么定义
可以先准备一个基础模板,例如 layout.html,把公共页面骨架集中放在这里。
<!-- application/view/layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{$title}</title>
</head>
<body>
<header><h1>{$header}</h1></header>
<main>{$content}</main>
<footer><p>{$footer}</p></footer>
</body>
</html>
子模板如何覆盖内容块
子模板则只保留当前页面需要替换的部分,通过 {extend} 和 {block} 指定继承关系与内容块。
<!-- application/view/index.html -->
{extend name="layout" /}
{block name="title"}首页{/block}
{block name="header"}欢迎来到首页{/block}
{block name="content"}
<h2>这是一个子模板</h2>
<p>{$message}</p>
{/block}
{block name="footer"}© 2023 公司名称{/block}
这种方式适合列表页、详情页、后台页面等有统一框架的场景。公共结构集中维护,具体页面只改动自己的内容区,后期调整导航、页脚或整体布局时会轻松很多。
模板变量和标签适合处理哪些动态内容
除了直接输出单个变量,ThinkPHP 模板还支持标签语法,常见场景就是用 volist 遍历数组并生成列表。对于商品列表、文章列表、用户列表这类数据展示页面,这比手工拼接 HTML 更清晰。

<!-- application/view/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{$title}</title>
</head>
<body>
<h1>{$header}</h1>
<p>{$message}</p>
<ul>
{volist name="list" id="item"}
<li>{$item.name} - {$item.price}</li>
{/volist}
</ul>
</body>
</html>
控制器中需要传入对应的数据结构:
// application/controller/Index.php
namespace appcontroller;
use thinkController;
class Index extends Controller
{
public function index()
{
$data = [
'title' => '首页',
'header' => '欢迎来到首页',
'message' => 'Hello, ThinkPHP!',
'list' => [
['name' => '商品1', 'price' => '100元'],
['name' => '商品2', 'price' => '200元'],
],
];
return $this->fetch('index', $data);
}
}
这里的重点是数据结构和模板标签要一一对应:模板里遍历的是 list,控制器就必须提供名为 list 的数组;模板里读取的是 {$item.name} 和 {$item.price},数组项也要有对应键名。
一套完整的 ThinkPHP 模板流程怎么理解
把上面的内容串起来,ThinkPHP 模板引擎的实际使用流程可以概括为:先在配置中定义视图规则,再创建模板文件,然后由控制器通过 fetch 完成渲染和传值;如果页面结构重复,就引入模板继承;如果页面要展示集合数据,就结合模板变量与 volist 等标签输出。
对日常开发来说,这几项已经覆盖了大多数基础需求。是否需要继续扩展,更关键的判断标准不是“模板语法会不会写”,而是当前页面到底属于简单单页渲染、公共布局复用,还是动态列表展示。









