ThinkPHP内置的模板引擎来定义模板文件,以及使用加载文件、模板布局和模板继承等高级功能。每个模板文件在执行过程中都会生成一个编译后的缓存文件,其实就是一个可以运行的
PHP文件。
开门见山
PHP
/*index/controller/example.php*/
use thinkController;
class test extends Index {
public function template (){
$name = 'Jerry';
$allName = [
0 => "Jerry",
1 => "Tom"
];
$this->assign('nameA',$name);
$this->assign('allNameA',$allName);
return $this->fetch('example');
}
}HTML
/*index/view/example.html*/
<span>{$nameA}</span>
{volist name="allNameA" id="vo"}
<p data-sub="{$key}" data-index="{$i}">{$vo}</p>
{/volist}
Result
<span>Jerry</span>
<p data-sub="0" data-index="1">Jerry</p>
<p data-sub="1" data-index="2">Tom</p>这样 ThinkPHP 便完成了对页面的解析工作,其中涉及到 模板实例化、定位、赋值、渲染及标签的使用。
视图实例化
暂无
模板赋值
显然在在模板中直接使用 $name 是找不到该变量的。必须使用 assign 方法对模板赋值。在执行渲染输出函数前,assign 方法使 函数内部变量与模板变量建立起映射关系。
$this->assign('nameA',$name);
$this->assign('allNameA',$allName);通过赋值之后,便可以使用自定义的 {$nameA} 来输出该变量了。
模板渲染
当数据已经准备好渲染到页面上,还需要指定 html 文件作为渲染模板,为当前函数执行 fetch 方法时候传入 example 参数。
return $this->fetch('example');//example is a html file.模板标签
在模板文件中使用的内置标签可以帮助我们在模板中循环、判断变量,它以一对花括号 {tagName} {/tagName}作为开标签和闭标签。 标签的{ 与 }之间必须紧跟标签属性或值,存在 空格 和 换行 都不能被正常解析。
volist 标签就是内置标签中的其中一个,通常用来循环某个数组变量。
{volist name="allNameA" id="vo"}
<p data-sub="{$key}" data-index="{$i}">{$vo}</p>
{/volist}
allName 是通过模板赋值的变量 ,现在可以在模板上直接使用了,显然它是一个数组,volist 标签对它做了循环操作。
volist 标签常用的一些属性:
| prop | Array Name | Current Ele |
|---|---|---|
| key | name | id |
| value | allName | vo |
隐藏在 volist 中的有几个内部变量 {$key} {$i} ,分别代表了 数组下标 和 循环次数。
与 volist 一样好用的内置标签还有很多:
内置标签
比较功能:
{eq name="data.status" value="0"}
0
{else/}
not equal 0
{/eq}
eq 标签像是一个 if 语句,同样能判断变量的值是否相同;而 neq 标签则相反。
条件判断功能:
{switch name="data.status"}
{case value="0"}0{/case}
{case value="1"}1{/case}
{case value="2"}2{/case}
{default /}10
{/switch}
switch 标签否相等。
比较 data.status 对象中 status 的值是否等于 value的值,等于则执行对应标签内的内容;
若所有 {case} 都不符合,则执行 {default /} 。
{if condition="boole_one"} value1
{elseif condition="boole_two"/}both
{else /}nothing
{/if}
当然还有复杂一点的 if 标签,用于判断复杂表达式是否成立。
其他:
//HTML
{assign name="username" value="Tom" /}
<p>{$username}</p>assign 标签能在渲染模板时在模板中声明变量,以便在渲染时直接使用。该标签往往配合其他标签使用。
{notempty name="username"}{/notempty}notempty 判断 username 变量是否存在, 不存在则进入下一层
{empty name="username"}{/empty}empty 判断 username 变量是否存在, 存在则进入下一层
显然,相互嵌套的标签可以达到逻辑控制的效果,避免重复编写模板文件。
模板继承
在开发后台系统时,可能非常多管理页面都是基于一个共同的布局。侧边栏、顶部栏、页脚都是相同的布局,这些代码可以共用一个页面文件,使用 {block}{/block} 标签可以实现。
不妨先写一个基础的模板:
//base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
{block name="style"}
{/block}
</head>
<body>
<div class="wrapper">
{block name="wrapperHeader"}
{/block}
{block name="content"}
{/block}
{include file="Public:footer" /}
</div>
{block name="script"}
{/block}
</body>
</html>
在 base.html 中编写好后台页面的整体结构, 页面中有时候不止有一个 block 块,在不同位置可以插入样式、内容或者脚本,这很大的原因决定于一些前端页面的规划,比如基于 bootstrap 框架的 AdminLTE。
对于 {block name="content"}{/block} 来说,在控制器中 fetch("example") 页面时,example.html 得先通过 {extend} 声明是要继承 base.html 页 。
//example.html
{extend name="Public:base" /}
{block name="content"}
<section class="content">
<p>This is example.html</p>
</section>
{/block}
在约定好的地方使用 {block name="content"}{/block} 以替换 .wrapper 下的 content 块。
继承事先写好的 base.html ,这种用法在编写模板页文件时会减少非常多的代码量。
//result base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="wrapper">
<section class="content">
<p>This is example.html</p>
</section>
</div>
</body>
</html>
引入模板
啥? {block} 不够强力? {include /} 可能是拆分模板的好方法。
当业务中需要查看当前的详细信息时,信息页往往由非常多数据组成。如果按照权限划分,显示给用户是不同的一些信息,出现的可能性将会更多。
直接在模板中引入其他模板文件,诸多模板文件成为了组件,减少了模板之间的冗余。
//exampleOrder.html
<section class="content">
<!--orderInfo-->
{include file="Public/order/orderInfo"/}
<!--orderTableCount-->
<div class="box">
{include file="Public/order/orderTableCount"/}
</div>
<div class="row">
<!--orderRecord-->
{include file="Public/order/orderRecord"/}
<!--orderImage-->
{include file="Public/order/orderImage"/}
</div>
</section>
Public/order 目录下的模板文件 orderInfo 、orderTable 、 orderRecord 、 orderImage 都被引入到了 exampleOrder.html 中。
orderInfo.html
<p>this is a order info page.</p>
orderRecord.html
<p>this is a order record page.</p>
orderImage.html
<p>this is a order image page.</p>
orderTable.html
<table class="table table-bordered table-hover">
<tbody>
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>
{volist name="list" id="vo"}
<tr>
<td>{$vo.name}</td>
<td>{$vo.age}</td>
<td>{$vo.country}</td>
</tr>
{/volist}
</tbody>
</table>
在被引入的页面中可以正常使用任何的 模板标签 或继续 引入 其他模板。
这样通过引用固定的模板,避免多个相同的页面存在,往后修改一处地方,引用该页面的模板也会做出相应的变动,对业务系统的修改起来非常省心。
如果渲染的父页面存在差异性,在父页面编写差异代码,则需继续细化引入的子页面,子页面再 {include /} 子页面能解决问题,但这会增加模板文件的数量。
<!--result exampleOrder.html-->
<section class="content">
<!--orderInfo-->
<p>this is a order info page.</p>
<!--orderTableCount-->
<div class="box">
<table class="table table-bordered table-hover">
<tbody>
<tr>
<th>Name</th>
<th>Age</th>
<th>Country</th>
</tr>
<tr>
<td>Jane</td>
<td>13</td>
<td>USA</td>
</tr>
<tr>
<td>xiaoMing</td>
<td>14</td>
<td>China</td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<!--orderRecord-->
<p>this is a order record page.</p>
<!--orderImage-->
<p>this is a order image page.</p>
</div>
</section>
思考与抉择
放眼一看,模板嵌套和模板引入都能实现差不多的需求,却是存在不同的差异。
模板继承 {block} 渲染(fetch)的是一个子页面,子页面继承了原有的主体框架。
引入模板 {include /} 渲染的是一主页面,在主页面中引入子页面来共同组成一个新页面。
当然,渲染页面不仅仅局限于这来两个种方案,还有非常多的组合方式。
在传统 MVC 应用中,页面的渲染功能往往交由后端完成,模板引擎具有的一些功能使得渲染页面变得更加方便,这意味着前端的任务仅仅是编写 HTML + CSS + JavaScript 组合式的代码吗? 为了进一步提升用户交互,我们有了 Ajax iframe,可是在前端并不满足处于被动状态的视图渲染方式。
早些年,一些前端应用框架逐渐发展起来,企图夺取页面的渲染权,掌管与后端的数据交换,为复杂的单页面应用提供驱动。这样,前端应用变得更像移动端的 App 一样百花怒放。
Vue.js 便是一款前端现代化框架。