6

ThinkPHP5 模板引擎 使用备忘

 3 years ago
source link: http://misaka.im/index.php/archives/11/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ThinkPHP5 模板引擎 使用备忘

2017.05.06默认分类 0 评

ThinkPHP 内置的模板引擎来定义模板文件,以及使用加载文件、模板布局和模板继承等高级功能。

每个模板文件在执行过程中都会生成一个编译后的缓存文件,其实就是一个可以运行的 PHP 文件。

引用自 http://www.kancloud.cn/manual/thinkphp5/118122

/*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');
    }
}
/*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 标签常用的一些属性:

propArray NameCurrent ElekeynameidvalueallNamevo

隐藏在 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 目录下的模板文件 orderInfoorderTableorderRecordorderImage 都被引入到了 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

{volist name="list" id="vo"} {/volist} Name Age Country {$vo.name} {$vo.age} {$vo.country}

在被引入的页面中可以正常使用任何的 模板标签 或继续 引入 其他模板。

这样通过引用固定的模板,避免多个相同的页面存在,往后修改一处地方,引用该页面的模板也会做出相应的变动,对业务系统的修改起来非常省心。

如果渲染的父页面存在差异性,在父页面编写差异代码,则需继续细化引入的子页面,子页面再 {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 便是一款前端现代化框架。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK