一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Javascript简单实现模板引擎例子

时间:2015-05-11 编辑:简简单单 来源:一聚教程网


一、背景

有两个大项目是从RD那边迁移过来,因为项目初期FE无人力跟进,所以都是后端同学直接用Smarty完成的前端部分;所以考虑到迁移的成本和方案,索性升级一下当前的JS模板引擎。
二、大致方案

支持extends标签

支持block标签

1、标签语法

为了便于模板词法分析,在模板左定界符后加上@来标识,标签名替换为属性设置方式,如extends标签:

<%@ extends="layout/layout.html" %>

2、extends实现方案

将整个extends标签替换成其指定的父模板的内容

3、block实现方案

首先理解block的概念:在父模板layout.html中提前挖好的坑,子模板中对block的实现,就好比萝卜,最后渲染的时候,就是一个萝卜一个坑的填充。

这样一来事情就好办多了,可将block的定义编译成js function的调用,假设父模板中block的定义是:
<%@ block="block_header" %>
最终可将其编译成:
block_header();
4、block进阶:父模板支持默认内容

smarty中的block是支持默认内容的,比如:(为了将smarty和js模板引擎区分开,下面用<&和&>分别标识smarty的定界符)
<&block name="block_header" &>
   


        ...
   

<&/block&>

那么,我们可以对应的在js模板引擎中支持如下格式:

<% block="block_header" {%>
   


        ...
   
   
<%}%>

没错,用一个相对投机的{和}将block默认内容包围起来,编译时,将block_header编译成一个function即可,为了避免和子模板中的block实现(也是一个function)命名冲突,所以给父模板中的block name定义加上parent标识,如:
function block_header__parent_ (){
    __html += '

\n';
    __html += '\t...\n';
    __html += '';
}
这样,如果子模板中没有对该block的(function)实现,那么这里可以直接执行这个blockx__parent方法即可。最终的样子差不多是这样的:
block_header__parent_();
function block_header__parent_ (){
    __html += '
\n';
    __html += '\t...\n';
    __html += '';
}

5、block进阶:子模板有block实现,甚至设置prepend、append模式

如题,假设子模板中有对该block的实现,可以用function来解决,如:

<%function block_header(){%>
    我是header
<%}%>
那么,按照smarty的理解,应该是要用子模板中的block来覆盖父模板的block的,所以父模板block的定义处可编译为:

block_header();
function block_header__parent_ (){
    __html += '

\n';
    __html += '\t...\n';
    __html += '';
}

而对于smarty中定义的prepend模式继承block,如:

<&block name="block_header" prepend &>
    我是header
<&/block&>
我们可以这样来简单模拟:
<%function block_header(prepend){%>
    我是header
<%}%>

对,通过给block_header添加prepend参数的方式来解决。对于这种形式,父母般block的定义处可编译为:

block_header();
block_header__parent_();
function block_header__parent_ (){
    __html += '

\n';
    __html += '\t...\n';
    __html += '';
}

同理,对于append模式,我们可以把它编译成:

block_header__parent_();
block_header();
function block_header__parent_ (){
    __html += '

\n';
    __html += '\t...\n';
    __html += '';
}

6、block进阶:多级继承(layout.html ← middle.html ← child.html)

逐级prepend或者append貌似不太好整,不过可以变换下思路,采用smarty中的block嵌套形式,比如:
<&block name="block_header" &>
    我是header

    <&block name="block_header_inner" &>
        我是inner
    <&/block&>
<&/block&>

所以,在middle.html中,可以先实现block_header,再在其内部继续挖坑定义block_header_inner,比如:

<%function block_header(){%>
      我是header
     <%@ block="block_header_inner" %>
<%}%>
这样,在子模板child.html中只需要实现middle.html中的block_header_inner即可:
<%function block_header_inner(){%>
      我是子模板中的inner
<%}%>

7、如何保证子模板中不会出现父模板未定义的东西?

这个是必须得去保证的,可以这样简单处理:在extends标签被替换的地方,强制加上:
return __html;

三、实践一下

1、layout.html



   
       
        Document
   
   
       


            <%@ block="block_header" %>
       

       


            <%@ block="block_content" {%>
               

                    这里是父layout中的内容
               

            <%}%>
       

       


            <%@ block="block_footer" %>
       

   

2、child.html

<%@ extends="layout.html" %>

<%function block_content(prepend){%>
    我是子模板中的内容
<%}%>

<%function block_footer(){%>
    我是子模板中的footer
<%}%>

我是子模板中多余的内容
3、编译后的中间文件

/*--/Users/zhaoxianlie/SourceCode/biz/trunk/demo/views/child.html--*/
exports.html = function ($_ROOT) {
    return function ($_DATA) {
        var __html = '';
        __html += '';
        __html += ' ' +
            ' Document ' +
            '

';

        __html += '

';
        block_content();
        block_content__parent_();
        function block_content__parent_() {
            __html += '
这里是父layout中的内容
';
        }
        __html += '
';
        block_footer();
        __html += '
';
        return __html;

        // 其实故事到这里就结束了。。。
        __html += '';
        function block_content(prepend) {
            __html += '我是子模板中的内容';
        }
        __html += '';
        function block_footer() {
            __html += '我是子模板中多余的内容';
        }
        __html += ' haha';
        return __html;
    };
};
4、运行以后

我是子模板中的内容
这里是父layout中的内容
我是子模板中的footer

差不多也就这点儿东西,就这么个原理,之后的smarty项目就好迁移了。