不要重複發明輪子,這是我聽到最多的一句話,並且如今有不少優秀的模板引擎: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>
至此,咱們的模板引擎的功能層面已經完成,能夠愉快的玩耍了。可是!還有不少優化工做等待着推動,這裏羅列幾條,週末再戰:
......