在平時編碼中,常常要作拼接字符串的工做,如把json數據用HTML展現出來,以往字符串拼接與邏輯混在在一塊兒會讓代碼晦澀不堪,加大了多人協做與維護的成本。而採用前端模板機制就能很好的解決這個問題。javascript
精妙的 tmplcss
前端模板類開源的很多,但最屬 jQuery 做者 John Resig 開發的 「javascript micro templating」 最爲精妙,寥寥幾筆便實現了模板引擎核心功能。 它的介紹與使用方式請看做者博客:http://ejohn.org/blog/javascript-micro-templating/html
麻雀雖小,五臟俱全,除了基本的數據附加外,還擁有緩存機制、邏輯支持。如今,若要我評出一個javascript 最節能的自定義函數排名,第一名是 $ 函數(document.getElementById 簡版),而第二名就是 tmpl 了。前端
固然,它並不是完美,我使用過程當中發現了一些問題:java
tmpl 美中不足git
1、沒法正確處理轉義字符,如: tmpl('<%=name%>\\<%=id%> ', {name:'糖餅', id: '1987'});它就會報錯。若正常工做,它應該輸出:糖餅\1987github
2、沒法識別數據裏的單引號web
3、設置變量默認值複雜,如正則表達式
tmpl('<%if(obj.name){%><%=name%><%}else{%>默認值<%}%> ', {name:'糖餅'}); //設置name默認爲 「默認值」json
tmpl 優化版本
廢話很少說,先敬上代碼:
1 function tmpl(str, data) { 2 var $ = '$' + (+ new Date) 3 , fn = function (data) { 4 var i, variable = [$], value = [[]]; 5 for (i in data) { 6 variable.push(i); 7 value.push(data[i]); 8 } 9 return (new Function(variable, fn.$)) 10 .apply(data, value).join(""); 11 }; 12 13 //將模板解析成函數 14 fn.$ = fn.$ || $ + ".push('" 15 + str.replace(/\\/g, "\\\\") 16 .replace(/'/g, "\\'") //防止單括號錯誤 17 .replace(/[\r\t\n]/g, " ") 18 .split("[:").join("\t") 19 .replace(/((^|:])[^\t]*)'/g, "$1\r") 20 .replace(/\t=([^\?]*?):]/g, "',$1,'") 21 .replace(/\t=([^\?]*?)\?(.*?):]/g, "',this.$1||'$2','") // [:=data?:] [:=data?任何內容:] 22 .split("\t").join("');") 23 .split(":]").join($ + ".push('") 24 .split("\r").join("\\'") 25 + "');return " + $; 26 27 //若是未定義data則返回編譯好的函數,使用時直接傳入數據便可, 28 //省去每次解析成函數的時間 29 return data ? fn(data) : fn; 30 31 };
好吧,上面的代碼看起來超出了4行,原諒我標題黨。不過這段代碼通過壓縮後,確實只有四行^_^。下面咱們就來詳細解構它。
首先看一下使用示例:
1 //循環結構 2 3 var tpl = '[: for(var k in ary){ var one=ary[k]; :]' 4 + '<p>[:=one:]</p>' 5 + '[: } :]'; 6 var data = {ary:[123,'abc']}; 8 var div = tmpl(tpl,data); 10 console.log(div); //</p>123</p><p>abc</p> 11 12 13 //變量驗證 14 15 var tpl = '[: if(this.name!==undefined){ :]' //注意必須使用 this.name,直接使用name,若是未定義就會報錯 16 + '<p>[:=name:]</p>' 17 + '[: } :]'; 18 var data = {name:'abc'}; 20 var div = tmpl(tpl,data); 22
23 //你還能夠這樣方便地使用未定義的變量: 24
25 var tpl = '<p>name:[:=name?:], name:[:=name?默認值:]</p>'; 26 var data = {no:'abc'}; 27 var div = tmpl(tpl,data); 28 29 console.log(div); // <p>name:, name:默認值</p> 30 31 32 //緩存編譯結果 33 34 35 var tpl = '[: for(var k in ary){ var one=ary[k]; :]' 36 + '<p>[:=one:]</p>' 37 + '[: } :]'; 38 var data = {ary:[123,'abc']}; 40 var render = tmpl(tpl); //不傳入data,則生成緩存,屢次使用緩存節約大量正則運算 42 var div = render(data); //傳入data,代入變量,解析成最終結果 43 44 console.log(div); //<p>123</p><p>abc</p>
使用方法:在 [: 與 :] 之間使用任何js代碼,而且經過 [:=data:] 方式以字符串形式輸出變量。更加詳細的使用方法/手冊,請查看:http://docs.codekart.jojoin.com/p/tool_tmpl
優化的地方:
一. 正確處理轉義字符 \ ' 等轉義字符
二. 修改包裹符 <% %> 爲 [: :] 防止與 html標籤</>和求餘運算符%產生衝突。
三. 修改環境變量 obj 爲 this
tmpl('<%if(obj.name=="name")%>') //舊版本
tmpl('[: if(this.name=="name") :]') //新版本
四. 爲變量添加默認值
tmpl('<%if(obj.name){%><%=name%><%}else{%>默認值<%}%>') //舊版本
tmpl('[:=name?默認值:]') //新版本
五. 去掉 with 語句,大幅提高引擎性能
六. 刪除無關緊要的功能,保持精簡
七. 增長調試模式
//打印模板編譯中間結果:
console(tmpl('<p>[:=name:]</p>').$);
//$1408707567855.push('<p>',name,'</p>');return $1408707567855
此引擎函數大體分爲兩部分:
一. 上半部分:模板函數解析執行
二. 下半部分:正則運算生成模板函數
能夠看出,引擎實現的重點主要在下半部分的一堆正則表達式,也就是「模板編譯」的過程。這裏我不打算把每個正則的功能都說清楚,那樣篇幅太大(好吧是我懶),各位看官請自行閱讀研究。
另外,此模板引擎已被集成到 Node.js web開發框架 Codekart 中。
Codekart 是一套給 Node.js 開發者使用的應用程序開發框架和工具包。 它提供一套豐富的標準庫以及簡單的接口和邏輯結構, 其目的是使開發人員更快速地進行項目開發。 使用 Codekart 能夠減小代碼的編寫量, 並將你的精力投入到項目的創造性開發上。
它已經幫你出色的完成了下面這些事情:
優雅的框架思惟
若是你須要一個真正的框架,而不是一個模塊/中間件/工具箱,若是你須要簡約與便捷,須要一目瞭然、理所固然的溫馨感,那麼 Codekart 將是最好的選擇。
高性能 HTTP 服務器
Codekart 處理 http 請求的性能接近原生 Node.js 代碼: http.createServer(), 緣由是框架只是對此函數作了簡單的封裝,其性能的損耗僅僅只有一個 url 正則匹配運算,路由請求處理程序。
便捷的靜態文件服務器
把文件放入 static/ 目錄下,啓動Codekart,url 訪問,搞定!
web 頁面模塊化支持
實際上,這是Codekart最出色的部分!它是先後端一體化的,能夠像寫配置文件同樣編寫web頁面, 框架自動完成 js、css 、tpl 文件的模塊化加載、合併、壓縮, 並在html裏引用,自動完成 html 模板的解析,而且支持頁面繼承和多態,一切就是那麼簡單輕鬆!
豐富的工具箱
Codekart 準備了一系列強大的先後端工具集合,涉及進程通訊,數據緩存,文件讀取,文件上傳,數據採集與處理,流程控制,任務計劃等諸多方面。
框架源碼託管在Github:https://github.com/yangjiePro/Codekart 歡迎提交新的代碼!