一步一步实现一个前端模板引擎

不要重复发明轮子,这是我听到最多的一句话,并且如今有不少优秀的模板引擎:handlebar、ejs、artTemplate...那么为何还要本身实现一个呢?缘由不外乎有两个,
一来是手痒,二来是知足一点小小的虚荣心:看,模板引擎我也会,简单!感受很是优(zhuang)秀(bi)。html

既然是本身动手,那么网上的教程确定先放一边,忽然有点耗子啃南瓜——无从下口的感受...前端

一切从需求出发

从后台拿到数据,拼接成字符串放在页面中,这是咱们初入前端时常要作的工做,特别是遇到结构稍微复杂的页面,光拼接字符串都能搞得你一脸懵逼、二脸懵逼,终于
有一天遇到模板引擎,一边惊为天人,一边暗自骂本身傻逼。那么,今天咱们动手实现的模板引擎,就从那最初的那一天开始吧!正则表达式

字符串模板

话说有天接到需求,须要将一组JSON数据,渲染到页面中。以下所示:函数

var data = [
    { text: 'text1' ,status:'done' },
    { text: 'text2' ,status:'pending' }
];

var tpl = '<ul>'+
    '<%for(var i = 0, len = data.length; i < len; i++) {%>'+
    '<li class="<%= data[i].status%>"><span><%= data[i].text%></span></li> '+
    '<%}%>'+
    '</ul>';

最初的渲染函数

机智如我天然想到用函数来循环。。。性能

var render = function(data) {
    var tmp = '';
    tmp += '<ul>';
    for(var i = 0, len = data.length; i < len; i++) {
        tmp += '<li class="'+ data[i].status +'">'+data[i].text+'</li>';
    }
    tmp += '</ul>';
    return tmp;
};

目前来说,咱们返回了渲染好的字符串,并且看来工做的很顺利。但若是将字符串增长点内容,这个函数就GG思密达了。由此看来,咱们须要把字符串模板单独提取出来,而后再
进行数据渲染。优化

牛B的Function

咱们用的最多的就是 function 关键字了,但对于 function 的爸爸 Function 却有点陌生,那么 Function 究竟哪里流弊呢?红宝石书不是建议咱们不要用 Function吗?
其实,在JS中,但咱们使用 function 声明函数的时候,JS会自动调用 Function 来生成实例。而且,Function 为咱们提供了更强大的武器——动态函数。spa

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

等同于htm

var function_name = function(arg1,..., argN) {function_body}教程

因而,咱们就有了一把强力的武器,将动态的字符串,放在动态的函数中执行了。

提取字符串构造函数体

有了前面的知识基础,这一步,咱们就要把 tpl 中的字符串,变成 render 的函数体。这就须要另外一把武器——正则表达式。利用它,来找到须要渲染数据的位置。

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

而后,经过 replace 方法替换 reg 找到的位置,构形成函数体!

var template = function(tpl) {
    var reg = /<%([\s\S]+?)%>/g;
    // index 用来记录替换的位置
    var index = 0;
    // 须要构造的函数体(一步一步和上面的render函数对比)
    var func_body = "var tmp = '';";
    func_body += "tmp += '";

    tpl.replace(reg, function(match, val, offset, str){
        // 每一次匹配到后,截取当前匹配位置和上一次匹配完成后位置之间的字符串
        func_body += tpl.substring(index, offset);

        // 根据 %= 判断如何进行拼接函数体
        if(match.indexOf('%=') < 0) {
            func_body +="';" + val + ";tmp += '";
        } else {
            func_body += "' + " + val.replace('=', '').trim() + "+'";
        }

        // 完成一次match,改变index 的值
        index = offset + match.length;
        return index;
    });

    // 完成全部匹配后,将剩下的字符串加入
    func_body += tpl.substring(index);
    // 返回 tmp
    func_body += "';return tmp;";
    return func_body;
};

如今,只要咱们调用 template 函数,就会返回如 render 的函数体相似的字符串。要使template 函数返回的字符串运行起来,就要用到 Function 了。

var tmpEngine = function (tpl, data) {
    // 返回字符串函数体
    var func_body = template(tpl);
    // 经过 Function 运行
    return new Function('data', func_body).call(null, data);
};

因而,咱们调用 tmpEngine, 就能够获得通过数据渲染后的字符串了。

var m = render(tpl2, data2);

console.log('m:' +m);

// m: <ul><li class="done"><span>text1</span></li> <li class="pending"><span>text2</span></li> </ul>

至此,咱们的模板引擎的功能层面已经完成,能够愉快的玩耍了。可是!还有不少优化工做等待着推动,这里罗列几条,周末再战:

  • 特殊字符转义,业务可能须要输出html代码,减小XSS攻击
  • 数据为空时的处理
  • 性能

......

相关文章
相关标签/搜索