[总结] js 模板引擎

嗯,这也是一个进修笔记,材料均来自收集,有修改。。
文章重要分为三部份,第一部份经由历程完成一个浅易的ERB模板引擎来引见其道理,参考javascript模板引擎和完成道理,有修改,看了很多关于模板引擎的文章,就这一篇最通俗易懂,合适入门。第二部份引见一个异常NB的模板引擎,参考JavaScript template engine in just 20 lines,超等简约,仅20行,合适进阶。第三部份引见一种js模板引擎——art Template,之所以引见他是由于。。。。你猜。

一.入门-浅易的ERB模板引擎

模板简介

模板平常是指嵌入了某种动态编程言语代码的文本,数据和模板经由历程某种情势的连系,可以变化出差异的结果。模板平常用来定义显现的情势,可以使得数据展示更加雄厚,而且轻易保护。比方,下面是一个模板的例子:

<ul>
    <% for(var i in items){ %>
        <li class='<%= items[i].status %>'><%= items[i].text %></li>
    <% } %>
</ul>

假如有以下items数据:

items:[
    { text: 'text1' ,status:'done' },
    { text: 'text2' ,status:'pending' },
    { text: 'text3' ,status:'pending' },
    { text: 'text4' ,status:'processing' }
]

经由历程某种体式格局的连系,可以发生下面的Html代码:

<ul>
    <li class='done'>text1<li>
    <li class='pending'>text2<li>
    <li class='pending'>text3<li>
    <li class='processing'>text4<li>
</ul>

假如不运用模板,想要到达一样的结果,行将上面的数据展示成结果的模样,须要像下面如许将html标签拼接成字符串:

var temp = '<ul>';
for(var i in items){
    temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
}
temp += '</ul>';

可以看出运用模板有以下优点:

  • 简化了html的誊写

  • 经由历程编程元素(比方轮回和前提分支),对数据的展示更具有掌握的才能

  • 星散了数据与展示,使得展示的逻辑和结果更容易保护

模板引擎

经由历程剖析模板,将数据和模板连系在一起输出末了的结果的顺序称为模板引擎,模板有很多种,相对应的模板引擎也有很多种。一种比较陈旧的模板称为ERB,在很多的web框架中被采纳,比方:ASP.NETRails … 上面的例子就是ERB的例子。在ERB中两个中心的观点:evaluateinterpolate。表面上evaluate是指包含在<% %>中的部份,interpolate是指包含在<%= %>中的部份。从模板引擎的角度,evaluate中的部份不会直接输出到结果中,平常用于历程掌握;而interpolate中的部份将直接输出到结果中。
从模板引擎的完成上看,须要依靠编程言语的动态编译或许动态诠释的特征,以简化完成和进步机能。比方:ASP.NET应用.NET的动态编译,将模板编译成动态的类,并应用反射动态实行类中的代码。这类完成实际上是比较复杂的,由于C#是一门静态的编程言语,然则运用javascript可以应用Function,以少少的代码完成一个浅易的模板引擎。

模板文本转化

针对上面的例子,回忆一下运用模板和不运用模板的差异。
模板写法:

<ul>
    <% for(var i in items){ %>
        <li class='<%= items[i].status %>'><%= items[i].text %></li>
    <% } %>
</ul>

非模板写法:

var temp = '<ul>';
for(var i in items){
    temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
}
temp += '</ul>';

仔细观察,实际上这两种要领非常“类似”,可以找到某种意义上的一一对应。假如可以将模板的文本变成代码实行,那末就可以完成模板转化。在转化历程当中有两个准绳:

  • 碰到一般的文本直接当做字符串拼接

  • 碰到interpolate(即<%= %>),将个中的内容当做变量拼接在字符串中

  • 碰到evaluate(即<% %>),直接当做代码

将上面的例子依据上述准绳举行变更,再增加一个总的函数:

var template = function(items){
    var temp = '';
    //最先变更
    temp += '<ul>';
    for(var i in items){
        temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
    }
    temp += '</ul>';
}

末了实行这个函数,传入数据参数即可:

var result = template(items);

javascript动态函数

可见上面的转化逻辑实在非常简朴,然则症结的题目是,模板是变化的,这意味着天生的顺序代码也必需是在运行时天生并实行的。幸亏javascript有很多动态特征,个中一个壮大的特征就是Function
我们平常运用function症结字在js中声明函数,很罕用Function。在jsfunction是字面语法,js的运行时会将字面的function转化成Function对象,所以实际上Function供应了更加底层和天真的机制。

Function 类直接建立函数的语法以下:

var function_name = new Function(arg1, arg2, ..., argN, function_body)

比方:

//建立动态函数    
var sayHi = new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");
//实行    
sayHi('Hello','World');

函数体和参数都可以经由历程字符串来建立!So cool!有了这个特征,可以将模板文本转化成函数体的字符串,如许就可以建立动态的函数来动态的挪用了。

完成思绪

起首应用正则式来形貌interpolateevaluate,括号用来分组捕捉:

var interpolate_reg = /<%=([\s\S]+?)%>/g;
var evaluate_reg = /<%([\s\S]+?)%>/g;

为了对全部模板举行一连的婚配将这两个正则式兼并在一起,然则注重,一切可以婚配interpolate的字符串都能婚配evaluate,所以interpolate须要有较高的优先级:

var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g

设想一个函数用于转化模板,输入参数为模板文本字串和数据对象

var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g
//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){ ... }

运用replace要领,举行正则的婚配和“替代”,实际上我们的目标不是要替代interpolateevaluate,而是在婚配的历程当中构建出“要领体”:

var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g
//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){
    var index = 0;//纪录当前扫描到哪里了
    var function_body = "var temp = '';";
    function_body += "temp += '";
    text.replace(matcher,function(match,interpolate,evaluate,offset){
        //找到第一个婚配后,将前面部份作为一般字符串拼接的表达式
        function_body += text.slice(index,offset);

        //假如是<% ... %>直接作为代码片断,evaluate就是捕捉的分组
        if(evaluate){
            function_body += "';" + evaluate + "temp += '";
        }
        //假如是<%= ... %>拼接字符串,interpolate就是捕捉的分组
        if(interpolate){
            function_body += "' + " + interpolate + " + '";
        }
        //递增index,跳过evaluate或许interpolate
        index = offset + match.length;
        //这里的return没有什么意义,由于症结不是替代text,而是构建function_body
        return match;
    });
    //末了的代码应该是返回temp
    function_body += "';return temp;";
}

至此,function_body虽然是个字符串,但内里的内容实际上是一段函数代码,可以用这个变量来动态建立一个函数对象,并经由历程data参数挪用:

var render = new Function('obj', function_body);
return render(data);

如许render就是一个要领,可以挪用,要领内部的代码由模板的内容组织,然则大抵的框架应该是如许的:

function render(obj){
    var temp = '';
    temp += ...
    ...
    return temp;
}

注重到,要领的形参是obj,所以模板内部援用的变量应该是obj

<script id='template' type='javascript/template'>
    <ul>
        <% for(var i in obj){ %>
            <li class="<%= obj[i].status %>"><%= obj[i].text %></li>
        <% } %>
    </ul>
</script>

看似到这里就OK了,然则有个必需处置惩罚的题目。模板文本中能够包含\r \n \u2028 \u2029等字符,这些字符假如出现在代码中,会失足,比以下面的代码是毛病的:

temp += '
        <ul>
    ' + ... ;

注重:javascript 中的字符串是不能跨行的!
我们愿望看到的应该是如许的代码:

temp += '\n \t\t<ul>\n' + ...;

如许须要把\n前面的\转义成\\即可,终究变成字面的\\n

别的,另有一个题目是,上面的代码没法将末了一个evaluate或许interpolate背面的部份拼接进来,处置惩罚这个题目的方法也很简朴,只须要在正则式中增加一个行尾的婚配即可:

var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$/g;

相对完全的代码

var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$/g

//模板文本中的特别字符转义处置惩罚
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
var escapes = {
    "'":      "'",
    '\\':     '\\',
    '\r':     'r',
    '\n':     'n',
    '\t':     't',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
  };

//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){
    var index = 0;//纪录当前扫描到哪里了
    var function_body = "var temp = '';";
    function_body += "temp += '";
    text.replace(matcher,function(match,interpolate,evaluate,offset){
        //找到第一个婚配后,将前面部份作为一般字符串拼接的表达式
        //增加了处置惩罚转义字符
        function_body += text.slice(index,offset)
            .replace(escaper, function(match) { return '\\' + escapes[match]; });

        //假如是<% ... %>直接作为代码片断,evaluate就是捕捉的分组
        if(evaluate){
            function_body += "';" + evaluate + "temp += '";
        }
        //假如是<%= ... %>拼接字符串,interpolate就是捕捉的分组
        if(interpolate){
            function_body += "' + " + interpolate + " + '";
        }
        //递增index,跳过evaluate或许interpolate
        index = offset + match.length;
        //这里的return没有什么意义,由于症结不是替代text,而是构建function_body
        return match;
    });
    //末了的代码应该是返回temp
    function_body += "';return temp;";
    var render = new Function('obj', function_body);
    return render(data);
}

挪用代码可所以如许:

<script id='template' type='javascript/template'>
    <ul>
        <% for(var i in obj){ %>
            <li class="<%= obj[i].status %>"><%= obj[i].text %></li>
        <% } %>
    </ul>
</script>

...

var text = document.getElementById('template').innerHTML;
var items = [
    { text: 'text1' ,status:'done' },
    { text: 'text2' ,status:'pending' },
    { text: 'text3' ,status:'pending' },
    { text: 'text4' ,status:'processing' }
];
console.log(template(text,items));

遗留的题目

另有几个细节的题目须要注重:

  • 由于<%或许%>都是模板的边境字符,假如模板须要输出<%或许%>,那末须要设想转义的方法

  • 假如数据对象中包含有null,明显不愿望末了输出null,所以须要在function_body的代码中斟酌null的状况

  • 在模板中每次运用obj的形参援用数据,能够不太轻易,可以在function_body增加with(obj||{}){...},如许模板中可以直接运用obj的属性

  • 可以设想将render返回出去,而不是返回转化的结果,如许外部可以缓存天生的函数,以进步机能

二.进阶-超简约js模板引擎

js模板引擎

var TemplateEngine = function(html, options) {
    var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0, match;//1
    var add = function(line, js) {//2
        js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
            (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
        return add;
    }
    while(match = re.exec(html)) {//3
        add(html.slice(cursor, match.index))(match[1], true);
        cursor = match.index + match[0].length;
    }
    add(html.substr(cursor, html.length - cursor));
    code += 'return r.join("");';
    return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);//4
}

诠释

标注1

re = /<%([^%>]+)?%>/g

该正则表达式用于猎取模板中的标识字段<%...%>,然后用传入引擎中的数据去添补它们.

reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g

该正则的作用是,假如一段JS代码以if, for, else, switch, case, break, |开首,那它们将会直接增加到函数体中。假如不是,则会被push到code变量中。

假如你看不懂上述正则表达式,请参考这里 js正则表达式语法

标注2

var add = function(line, js) {
        js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
            (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
        return add;
    }

js模板由三部份构成

1.一般文本字符串,比方标签元素ul
2.js流程掌握语句,比方if, for, else等等,对应ERB模板中的interpolate
3.变量,须要用输入的数据替代的部份,对应ERB模板中的evaluate

注重:有的将模板分为两部份,js代码(对应2)和文本(对应1、3)

解释2中add函数的作用是天生衬着函数的函数体字符串(Function函数体字符串),该函数终究返回HTML文档字符串。模板中的一切部份都须要拼接构成函数体字符串,但依据是不是须要拼接到HTML文档字符串中,须要对差异部份实行差异的操纵。js代码作为流程掌握语句,不须要拼接到HTML文档字符串中(函数返回的字符串中不会包含该部份),直接作为代码实行,而关于文本须要拼接成HTML文档字符串,具体剖析以下:

解释:拼接HTML文档字符串有两种体式格局,一种是应用+运算符,另一种是将须要拼接到HTML文档字符串的部份push到数组中,然后应用’join’函数兼并。

1.关于一般文本字符串,须要作为字符串拼接到HTML字符串中,此时,js取false,将实行

(code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');

注重push到数组中的内容须要被""围困的,因而假如一般文本中包含有",须要转译为\\"

2.关于流程掌握语句和变量,他们都是被标识标记<%%>围困的部份,对应的js去true,将实行

(code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') 

1)关于js流程掌握语句,line.match(reExp)true,不须要拼接到HTML文档字符串中,因而不须要push到数组中,而直接拼接到函数体字符串中。
2)关于变量,须要拼接到HTML文档字符串中,然则不能被""围困,否则会作为字符串输出,而不会作为变量。

别的:注重add函数末了又返回了本身,类似于js中的链式操纵。

标注3

while(match = re.exec(html)) {
        add(html.slice(cursor, match.index))(match[1], true);
        cursor = match.index + match[0].length;
    }

这里须要相识exec函数,不晓得请移步这里JavaScript exec() 要领

标注4

return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);

应用apply设置Function函数作用域,在函数内部,this指向options,可以应用this.attr接见options.attr。

三.art Template

简介

javascript模板引擎是数据与界面星散事情中最重要一环。虽然每一个引擎从模板语法、语法剖析、变量赋值、字符串拼接的完成体式格局各有所差异,但症结的衬着道理仍然是动态实行 javascript 字符串。

artTemplate 是新一代 javascript 模板引擎,它采纳预编译体式格局让机能有了质的奔腾,而且充分应用 javascript 引擎特征,使得其机能不管在前端照样后端都有极为精彩的表现。

art Template高效的隐秘

预编译

在平常的模板引擎完成道理中,由于要对模板变量举行赋值,所以每次衬着都须要动态编译 javascript 字符串完成变量赋值。而 artTemplate 的编译赋值历程倒是在衬着之前完成的,这类体式格局称之为“预编译”。

更快的字符串拼接体式格局

很多人误以为数组 push 要领拼接字符串会比 += 快,要知道这仅仅是 IE6-8 的浏览器下。实测表明当代浏览器运用 += 会比数组 push 要领快,而在 v8 引擎中,运用 += 体式格局比数组拼接快 4.7 倍。所以 artTemplate 依据 javascript 引擎特征采纳了两种差异的字符串拼接体式格局。

运用要领

  1. 援用js文件:<script src=”js/arttmpl.js”></script>

  2. 页面中,运用一个type=”text/template”的script标签寄存模板:

<script id='doctor-template' type="text/template">
        <% for(var i in data){ var item=data[i]; %>
            <li class="mui-table-view-cell mui-media ">
                <a href="javascript:;" class="mui-navigate-right">
                    <div class="mui-media-object mui-pull-left">
                        <img src="<%=(item.photo)%>" width="100%">
                        <span class="mui-badge mui-badge-danger"><%=(item.score)%>分</span>    
                    </div>
                    <div class="mui-media-body">
                        <%=(item.name)%>&nbsp;<span class="mui-badge mui-badge-warning"><%=(item.position)%></span>
                        <p class="mui-ellipsis"><%=(item.hospital)%></p>
                        <p class="mui-ellipsis"><%=(item.desc)%></p>
                    </div>
                </a>
            </li>
        <% } %>
    </script>

模板逻辑语法最先与完毕的界定标记为<% %>,若<%背面紧跟=号则输出变量内容。同ERB模板

3.衬着模板

template.render(id, data);

继承上面的例子:

var fragment = template('doctor-template', {
                "data":[
                    {
                        name:"王静",
                        score:4.5,
                        photo:'images/logo.png',
                        hospital:"江苏省中病院",
                        desc:'妇科、不孕不育症、月经病等',
                        position:'副医师'
                    },
                    {
                        name:"啦啦",
                        score:4.9,
                        photo:'images/logo.png',
                        hospital:"鼓楼病院",
                        desc:'儿童呼吸系统疾病的诊治,关于儿童疾病',
                        position:'主治医师'
                    }
                ]
            });

四.参考

1.javascript 模板引擎系列文章(一)
2.JavaScript模板引擎道理,几行代码的事儿
3.高机能JavaScript模板引擎道理剖析
4.javascript模板引擎和完成道理
5.js模板引擎——art Template
6.[译] 只要 20 行的 JavaScript 模板引擎
7.javascript模板引擎和完成道理
8.引荐五款盛行的JavaScript模板引擎

    原文作者:jack2wang
    原文地址: https://segmentfault.com/a/1190000005883875
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞