不要重复发明轮子,这是我听到最多的一句话,并且如今有不少优秀的模板引擎: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思密达了。由此看来,咱们须要把字符串模板单独提取出来,而后再
进行数据渲染。优化
咱们用的最多的就是 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>
至此,咱们的模板引擎的功能层面已经完成,能够愉快的玩耍了。可是!还有不少优化工做等待着推动,这里罗列几条,周末再战:
......