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 便是一款前端现代化框架。