全球最快的JS模板引擎

廢話很少說,先上測試:javascript

JS模板引擎性能測試

親測請訪問:【在線測試地址】單次結果不必定準確,請多測幾回。html

tppl 的編譯渲染速度是著名的 jQuery 做者 John Resig 開發的 tmpl 的 43 倍!與第二名 artTemplate 也有一倍的差距。前端

彷佛每個大公司都選擇本身開發模板引擎並將其開源,結果就是社區充斥着數不清的引擎,讓人眼花繚亂無從選擇。隨着時間的流逝,愈來愈多的功能被添加進去,最終讓一個強悍的發動機變成了一臺臃腫複雜零件生鏽的拖拉機。天吶,我就想網頁面裏插一段 html,你竟然要我往每一個js文件裏再塞進500行代碼!java

不,事情本來應該更簡單。保持代碼的簡潔高效也意味着讓生活更加健康愉悅。git

好吧,滿腦子裝着「封裝」或者「模塊化」的讀者估計有不一樣的見解。github

下面咱們來談談如何讓引擎更增強勁高效。正則表達式

模板引擎分爲兩大主要陣營:數組

  1. 原生語法
  2. 自定義模板語法

此兩種方案各有各的好,自定義語法相比前者的優勢在於,看起來和寫起來更加規範簡潔,更「像」是一種模板。也能更好的配合編譯,而且能夠避免用戶寫出「性能不佳」的代碼。部分人認爲自定義語法對頁面設計人員來講更爲友好,這就見仁見智了。而缺點就是一般只能自定義 if else 和 for 循環等簡單而有限的邏輯結構,不夠強大和靈活。瀏覽器

自定義語法的優化方法有隨着語法的不一樣而不一樣,但一般最終都是將其轉換爲原生的語言邏輯結構。這裏主要討論原生語法模板引擎的優化。緩存

對於追求性能的模板引擎來講,有兩個顯而易見的方向:

  1. 編譯結果緩存
  2. 編譯結果靜態化

緩存很好理解,一次編譯屢次渲染。一般是保存初步正則替換後的字符串中間值,重複渲染時直接拿來使用。

靜態指的是,編譯模板字符串生成一個字符串拼接的函數,而不是每次建立函數。渲染操做就至關於一次函數調用,代入變量完成字符串拼接並返回。通常引擎優化到這一步時,渲染方面已經沒有太大的差距和進步的空間。你們都成了字符串拼接函數,只能在微小的語法層面作優化。這裏說一下,相信從事過前端開發的朋友,都「據說」過一個字符串拼接的「快速方法」:將字符串片斷 push 進一個數組,最後再 join 返回,性能比直接採用 + 或者 += 字符串要好。注意,這種方法過期了!在現代瀏覽器以及Node.js中已經再也不成立。經過數組鏈接字符串只是一個「臨時解決方案」,隨着各大js編譯器的優化和進步,直接採用 + 字符串操做,給了一個編譯器在語言底層作出優化的機會,你們仍是着眼於將來吧。

渲染差距不大時,編譯則還存在不少「水分」能夠擠出來。固然,一部分模板引擎集成了諸如文件加載、Ajax等高級功能,對其性能方面有要求過高彷佛也不太合理。

通常原生語法模板引擎,都採用相似下面的字符串表示:

<h1> <%=title %> </h1> <% for(var i in content){ %> <p>第<%=i%>段:<%=content[i]%></p> <%}%>

而編譯操做就是將這一段字符串轉換成相似下面的函數:

function(data){ var str = "<h1> "+data.title+" </h1>"; for(var i in data.content){ str += "<p>第"+i+"段:"+data.content[i]+"</p>"; } return str; }

一般狀況都是改變字符串結構,去掉模板標籤,再使用 new Function() 建立函數。

傳統傳遞參數的實現經過遍歷數據對象,把對象的名值分離,而後分別把對象成員名稱做爲new Function的參數名(即變量名),而後使用函數的appley調用方式傳給那些參數。

tmpl 則使用了javascript不經常使用的 with 語句實現。 實現方式很簡潔,省去了var這個關鍵字。tmpl 性能問題就出在 with 上面。javascript 提供的 with 語句,本意是想用來更快捷的訪問對象的屬性。不幸的是,with語句在語言中的存在,就嚴重影響了 javascript 引擎的速度,由於它阻止了變量名的詞法做用域綁定。

而轉換的方法有不少種,一部分採用 split() 截斷分析,一部分採用全正則替換,更有強悍的引擎使用詞法分析,總之各顯神通。

全正則替換的方案只是一長串的 .replace() 鏈式調用,看起來代碼更加美妙,但因爲存在中間過渡狀態和方法而致使性能不佳。詞法分析更沒必要說,大量的正則拖慢編譯速度。編譯優化的重點就在,儘可能減小中間態,並減小複雜正則表達式的使用。通過實測,split() 截斷分析能減小一部分正則,性能更好。

tppl 一開始使用「模板尾標籤分割」,即:str.split(」%>」) 的方式,這與 tmpl 的實現方式不謀而合,上面的字符串被分割爲6段,而後爲每一段使用一次正則替換:

var tpls = ["<h1> <%=title", " </h1>","<% for(var i in content){ ", "<p>第<%=i", "段:<%=content[i]", "</p><%}"];

在後來的性能測試中,發現這種實現方式相比其它引擎,並無太大的提高。看來只能另闢蹊徑了。

通過長時間的左思右想,終於發現採用「模板頭標籤分割」的方式,能大大減小分割結果的數量,可是須要修改模板標籤:

<h1> [=:title:] </h1> [: for(var i in content){ :] <p>第[=:i:]段:[=:content[i]:]</p> [:}:]

方括號 [ 與冒號 : 組成的模板標籤相比 <% 能更加清晰的區分html代碼與js邏輯代碼。經過 .split(」[:」) 將模板分割爲3段:

var tpls = ["<h1> [=:title:] </h1>", " for(var i in content){ :]<p>第[=:i:]段:[=:content[i]:]</p>", "}:]"];

如此一來正則替換從6次降低到3次,性能提高將近一倍!並且隨着代碼邏輯結構的不一樣,性能提高將會更大。

關鍵的正則表達式:

var line = "'"+"<p>第[=:i:]段:[=:content[i]:]</p>".replace(/\[\=\:(.*?)\:\]/g, "'+$1+'")+"'"; // '<p>第'+i+'段:'+content[i]+'</p>'

tppl 的源碼託管在 Github 上,地址:https://github.com/yangjiePro/tppl

若是你還有更好編譯優化方法,歡迎討論!

相關文章
相關標籤/搜索