谈谈模板继承思想的实现(以freemarker为例)

一、什么是模板继承


模板继承不同于模板布局,甚至来说,应该在模板布局的上层。模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。

或者更确切的比喻,模板继承更像是java中的implement,而不是extent,尽管在大量phppythonruby框架中称之为:template extend

因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。每个区块由<block></block>标签组成(thinkphp为例),并且不支持block标签的嵌套。

模板继承,已广泛使用于诸如phppythonrubyweb框架中,但早先的模板如jstftl等并不原生支持模板继承。

 

二、为什么要考虑模板继承


首先对比一下几乎所有渲染模板都有的,模板布局。用流行性语言描绘,模板布局的思想,是一种不带交互的组件化思想。

模板布局应用最为广泛,譬如php中的requirefreemarker中的macro - <@xxx/>django中的{% include };用前端的模板加以对比,则如jstregular中的<#include >。其核心思想即将大部分页面的公共部分进行拆分,通过在不同页面中组装,达到模板复用的目的。

 例如:

    


这种模板布局的思想,可以解决大多数的问题,但是如果有大量同类页面,但每个页面又有些许差异时,这种模板组件的组装就逐渐体现成一种体力活。

 首先各个组件是耦合在最终生成的模板页面中的,譬如以下的代码逻辑:

<#include header />
<div>
<#include sidebar />
<div> 内容 。。。。。。</div>
</div>
<div>
内容 。。。。。。
</div>
<#include footer>

那么每修改内容部分就需要格外小心,不要误动了include组件。当然也可以把内容部分抽离出来单独封装成模板组件,组合载入。

另一个可以体现大量复用思想的场景(仅举例而已):业务有多个子应用,每个应用的首页基本结构相近,但是又各有差异。另外每个应用会有不同变化位置的广告位

那么使用常规思路就是分别为应用设计ABC三个页面(每个相互独立,引入组件),每个页面分别要include各自所需的组件(如图1号页面、2号页面等),当有广告活动时,需要再在ABC三个页面中添加对应的广告(可能是在页面中加入广告锚点),然后定义广告组件include进去。等到活动结束再把对应的广告锚点撤销。

那么再来对比使用模板继承的情形:

首先需要定义基类模板,诸如base.html

ABC三个页面继承于base.html,只需要重新定义ABC的差异部分

A模板(只需引入差异部分,无需引入1号页面、3号页面等):

         {% extend base.html %}

         {% block %}

             A页面逻辑

         {% block %}

         {% include 2号页面组件 %}

    同时ABC的带广告页面又继承于ABC,如A的活动模板:

         {% extend A.html %}

         {% block %}

             {% include 广告1 %}

         {% block %}

将广告逻辑与A页面逻辑剥离开,且切换方便,代码复用性强。对比于原来可能需要在A页面基础上添加广告位置(撤掉又需要在A页面上删除)或者拷贝A页面在改写(大量的代码冗余),相当便利。

模板继承的思想并不是抛弃原有的模板组件的使用,而是在其上做了加强,对于有大量同构页面,将模板继承和模板组合的思想灵活应用,必定事半功倍。


三、Thinkphpdjango(python)中的模板继承


1.Thinkphp中的模板继承


例如基模板:

1.       <html>

2.       <head>

3.       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

4.       <block name="title"><title>标题</title></block>

5.       </head>

6.       <body>

7.       <block name="menu">菜单</block>

8.       <block name="left">左边分栏</block>

9.       <block name="main">主内容</block>

10.     <block name="right">右边分栏</block>

11.     <block name="footer">底部</block>

12.     </body>

13.     </html>

对应继承模板:

1.      <extend name="base" />

2.       <block name="title"><title>{$title}</title></block>

3.       <block name="menu">

4.       <a href="/" >首页</a>

5.       <a href="/info/" >资讯</a>

6.       <a href="/bbs/" >论坛</a>

7.       </block>

8.       <block name="left"></block>

9.       <block name="content">

10.     <volist name="list" id="vo">

11.     <a href="/new/{$vo.id}">{$vo.title}</a><br/>

12.    {$vo.content}

13.     </volist>

14.     </block>

15.     <block name="right">

16.    最新资讯:

17.     <volist name="news" id="new">

18.     <a href="/new/{$new.id}">{$new.title}</a><br/>

19.     </volist>

20.     </block>

21.     <block name="footer">

22.    @ThinkPHP2012 版权所有

23.     </block>

2. Django中的模板继承使用

{% extends "base.html" %}

{% block title %}Articles for {{ year }}{% endblock %}

{% block content %}

<h1>Articles for {{ year }}</h1>

{% for article in article_list %}

    <p>{{ article.headline }}</p>

    <p>By {{ article.reporter.full_name }}</p>

    <p>Published {{ article.pub_date|date:"F j, Y" }}</p>

{% endfor %}

{% endblock %}


四、Freemarker实现模板继承


    Freemarker并不原生支持模板继承,但他具有完善的模板组件,称之为宏(macro)。而我们可以基于宏来实现模板继承。

    首先,我们探究一下什么是继承的核心?

    我理解的继承就是子类拥有父类所以的方法、特性,同时子类又可以重写父类的这些特性与方法,并拥有自己独特的方法。

    那么如果freemark的“基模板”本身就是一个宏,且可以同时内嵌组件化的宏呢?


    诸如:

        <#macro base>

        <@header/>

        <div>

             <@组件1/>

        </div>

        <@组件2/>

        <@组件3/>

        <@footer/>

        </#macro>

那么子类继承父类只需要重写其所不同的部分,如

        <#macro 组件1>

             <@组件4 />

        </#macro>

        <@base/>


那么即实现了对base的继承,同时定义了子模板自己的差异。其他不原生支持模板继承思想的模板体系,也可以参考。


注意:模板继承的思想并不是抛弃原有的模板组件的使用,而是在其上做了加强,对于有大量同构页面,将模板继承和模板组合的思想灵活应用,必定事半功倍!



参考示例:

django模板继承:http://python.usyiyi.cn/django/intro/overview.html#design-your-templates

thinkphp模板继承:http://www.thinkphp.cn/info/178.html


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者陈盛授权发布。