亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

javascript模版引擎-tmpl的bug修復(fù)與性能優(yōu)化分析

 更新時(shí)間:2011年10月23日 18:31:57   作者:  
在平時(shí)編碼中,經(jīng)常要做拼接字符串的工作,如把json數(shù)據(jù)用HTML展示出來(lái),以往字符串拼接與邏輯混在在一起會(huì)讓代碼晦澀不堪,加大了多人協(xié)作與維護(hù)的成本。而采用前端模板機(jī)制就能很好的解決這個(gè)問(wèn)題
精妙的 tmpl
前端模板類開(kāi)源的不少,但最屬 jQuery 作者 John Resig 開(kāi)發(fā)的 “javascript micro templating” 最為精妙,寥寥幾筆便實(shí)現(xiàn)了模板引擎核心功能。
它的介紹與使用方式請(qǐng)看作者博客:http://ejohn.org/blog/javascript-micro-templating/
讓我們先看看他的源碼:
復(fù)制代碼 代碼如下:

(function(){
var cache = {};
this.tmpl = function (str, data){
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
"with(obj){p.push('" +
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn( data ) : fn;
};
})();

麻雀雖小,五臟俱全,除了基本的數(shù)據(jù)附加外,還擁有緩存機(jī)制、邏輯支持。現(xiàn)在,若要我評(píng)出一個(gè)javascript 最節(jié)能的自定義函數(shù)排名,第一名是 $ 函數(shù)(document.getElementById 簡(jiǎn)版),而第二名就是 tmpl 了。
當(dāng)然,它并非完美,我使用過(guò)程中發(fā)現(xiàn)了一些問(wèn)題:
tmpl 美中不足
一、無(wú)法正確處理轉(zhuǎn)義字符,如:
復(fù)制代碼 代碼如下:
tmpl('<%=name%>//<%=id%> ', {name:'糖餅', id: '1987'});

它就會(huì)報(bào)錯(cuò)。若正常工作,它應(yīng)該輸出:糖餅/1987
實(shí)際上解決起來(lái)很簡(jiǎn)單,添加一行正則對(duì)轉(zhuǎn)義符進(jìn)行轉(zhuǎn)義:
復(fù)制代碼 代碼如下:
str.replace(/\\/g, "\\\\")

二、它有時(shí)候無(wú)法正確區(qū)分第一個(gè)參數(shù)是ID還是模板。
假若頁(yè)面模板ID帶有下劃線,如 tmpl-photo-thumb 它不會(huì)去查找這個(gè)名稱的模板,會(huì)認(rèn)為這傳入的是原始模板直接編譯輸出。
原始模板與元素id最直觀的區(qū)別就是是否含有空格,因此改動(dòng)下正則表達(dá)式即可:
view sourceprint?1 !/\s/.test(str)
三、它內(nèi)部還殘有一處測(cè)試用的代碼,可刪除。
復(fù)制代碼 代碼如下:
print=function(){p.push.apply(p,arguments);}

tmpl 效率的疑惑
直到前段時(shí)間看了百度mux一篇介紹 YayaTemplate 的軟文,原文作者對(duì)各大流行的模板引擎進(jìn)行了效率測(cè)試,最終得出 YayaTemplate 是最快的一個(gè)。 雖然測(cè)試結(jié)果 tmpl 不敵 YayaTemplate ,但也讓我打消了對(duì)性能的顧慮,實(shí)際應(yīng)用中與傳統(tǒng)的字符串拼接差不多。它們只有進(jìn)行超大規(guī)模的解析才會(huì)有較大的性能差距。(超大規(guī)模?javascript本身就不適合干這事。若哪天程序員一次性給瀏覽器插入上千條列表數(shù)據(jù)而其慢無(wú)比的時(shí)候,不用懷疑:?jiǎn)栴}出在了這個(gè)程序員身上,他不會(huì)愛(ài)惜用戶的瀏覽器。)
若說(shuō)到引擎效率排名問(wèn)題,我倒不覺(jué)得這是不能是衡量模板引擎的首要標(biāo)準(zhǔn),模板語(yǔ)法也是重要的一環(huán),這時(shí)候 YayaTemplate 的模板語(yǔ)法就顯得晦澀多了,它為了節(jié)省幾個(gè)正則表達(dá)式而在模板語(yǔ)法上耍了小聰明。
先展示 YayaTemplate 的源碼:
復(fù)制代碼 代碼如下:

//author:yaya,jihu
//uloveit.com.cn/template
//how to use? YayaTemplate("xxx").render({});
var YayaTemplate = YayaTemplate || function(str){
//核心分析方法
var _analyze=function(text){
return text.replace(/{\$(\s|\S)*?\$}/g,function(s){
return s.replace(/("|\\)/g,"\\$1")
.replace("{$",'_s.push("')
.replace("$}",'");')
.replace(/{\%([\s\S]*?)\%}/g, '",$1,"')
}).replace(/\r|\n/g,"");
};
//中間代碼
var _temp = _analyze(document.getElementById(str)?document.getElementById(str).innerHTML:str);
//返回生成器render方法
return {
render : function(mapping){
var _a = [],_v = [],i;
for (i in mapping){
_a.push(i);
_v.push(mapping[i]);
}
return (new Function(_a,"var _s=[];"+_temp+" return _s;")).apply(null,_v).join("");
}
}
};

若把性能問(wèn)題上升到一個(gè)“學(xué)術(shù)問(wèn)題”的高度嘗試去解決,為什么 tmpl 會(huì)比 YayaTemplate 慢?
語(yǔ)法解析?雖然 YayaTemplate 使用了一個(gè)新穎的 javascript 包裹 html 的方式作為模板語(yǔ)法,但最終都需要用正則表達(dá)式解析成標(biāo)準(zhǔn)的 javascript 語(yǔ)法,這里正則的效率不會(huì)有太大的差異,并且雙方都使用了緩存機(jī)制確保只對(duì)原始模板僅進(jìn)行一次解析。
數(shù)據(jù)轉(zhuǎn)換?模板引擎會(huì)把數(shù)據(jù)最終以變量的形式保存在閉包中,以好讓模板獲取到。這里先對(duì)比下一下雙方的變量聲明機(jī)制:
YayaTemplate 使用傳統(tǒng)傳遞參數(shù)的形式實(shí)現(xiàn)。它通過(guò)遍歷數(shù)據(jù)對(duì)象,把對(duì)象的名值分離,然后分別把對(duì)象成員名稱作為new Function的參數(shù)名(即變量名),然后使用函數(shù)的appley調(diào)用方式傳給那些參數(shù)。
tmpl 則使用了javascript不常用的 with 語(yǔ)句實(shí)現(xiàn)。 實(shí)現(xiàn)方式很簡(jiǎn)潔,省去了var這個(gè)關(guān)鍵字。
tmpl 性能問(wèn)題就出在 with 上面。javascript 提供的 with 語(yǔ)句,本意是想用來(lái)更快捷的訪問(wèn)對(duì)象的屬性。不幸的是,with語(yǔ)句在語(yǔ)言中的存在,就嚴(yán)重影響了 javascript 引擎的速度,因?yàn)樗柚沽俗兞棵脑~法作用域綁定。
優(yōu)化 tmpl
tmpl 若去掉 with 語(yǔ)句,而改用傳統(tǒng)的傳參性能立即大提升,經(jīng)過(guò)實(shí)測(cè)在24萬(wàn)條數(shù)據(jù)下 firefox 能提高 5 倍,chrome 2.4 倍,opera 1.84倍,safari 2.1倍,IE6 1.1倍,IE9 1.35倍,最終與 YayaTemplate 不分上下。
測(cè)試地址:http://www.planeart.cn/demo/tmpl/tmpl.html
tmpl 優(yōu)化版最終代碼:
復(fù)制代碼 代碼如下:

/**
* 微型模板引擎 tmpl 0.2
*
* 0.2 更新:
* 1. 修復(fù)轉(zhuǎn)義字符與id判斷的BUG
* 2. 放棄低效的 with 語(yǔ)句從而最高提升3.5倍的執(zhí)行效率
* 3. 使用隨機(jī)內(nèi)部變量防止與模板變量產(chǎn)生沖突
*
* @author John Resig, Tang Bin
* @see http://ejohn.org/blog/javascript-micro-templating/
* @name tmpl
* @param {String} 模板內(nèi)容或者裝有模板內(nèi)容的元素ID
* @param {Object} 附加的數(shù)據(jù)
* @return {String} 解析好的模板
*
* @example
* 方式一:在頁(yè)面嵌入模板
* <script type=&quot;text/tmpl&quot; id=&quot;tmpl-demo&quot;>
* <ol title=&quot;<%=name%>&quot;>
* <% for (var i = 0, l = list.length; i < length; i ++) { %>
* <li><%=list[i]%></li>
* <% } %>
* </ol>
* </script>
* tmpl('tmpl-demo', {name: 'demo data', list: [202, 96, 133, 134]})
*
* 方式二:直接傳入模板:
* var demoTmpl =
* '<ol title=&quot;<%=name%>&quot;>'
* + '<% for (var i = 0, l = list.length; i < length; i ++) { %>'
* + '<li><%=list[i]%></li>'
* + '<% } %>'
* +'</ol>';
* var render = tmpl(demoTmpl);
* render({name: 'demo data', list: [202, 96, 133, 134]});
*
* 這兩種方式區(qū)別在于第一個(gè)會(huì)自動(dòng)緩存編譯好的模板,
* 而第二種緩存交給外部對(duì)象控制,如例二中的 render 變量。
*/
var tmpl = (function (cache, $) {
return function (str, data) {
var fn = !/\s/.test(str)
? cache[str] = cache[str]
|| tmpl(document.getElementById(str).innerHTML)
: function (data) {
var i, variable = [$], value = [[]];
for (i in data) {
variable.push(i);
value.push(data[i]);
};
return (new Function(variable, fn.$))
.apply(data, value).join("");
};
fn.$ = fn.$ || $ + ".push('"
+ str.replace(/\\/g, "\\\\")
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join($ + ".push('")
.split("\r").join("\\'")
+ "');return " + $;
return data ? fn(data) : fn;
}})({}, '$' + (+ new Date));

模板引擎依賴 Function 構(gòu)造器實(shí)現(xiàn),它與 eval 一樣提供了使用文本訪問(wèn) javascript 解析引擎的方法,這也會(huì)讓性能顯著的降低,但此時(shí) javascript 中已別無(wú)他法。
使用 Function 構(gòu)造器還會(huì)對(duì)參數(shù)名稱有所限制,所以導(dǎo)致數(shù)據(jù)成員命名必須與 javascript 變量名規(guī)范保持一致,否則會(huì)報(bào)錯(cuò)。好在這個(gè)錯(cuò)誤可以在運(yùn)行的時(shí)候立馬被發(fā)現(xiàn),而不會(huì)成為一顆地雷。
tmpl 使用小竅門
一、緩存優(yōu)化。
tmpl 默認(rèn)對(duì)嵌入到頁(yè)面中的模板進(jìn)行了緩存優(yōu)化(即第一個(gè)參數(shù)為ID的時(shí)候),它只會(huì)對(duì)模板進(jìn)行一次分析。若原始模板是直接傳入到 tmpl 第一個(gè)參數(shù)中,且需要多次使用的話,建議用公用變量緩存起來(lái),需要解析數(shù)據(jù)的時(shí)候再使用,以獲得相同的優(yōu)化效果。如:
復(fù)制代碼 代碼如下:

// 生成模板緩存
var render = tmpl(listTmpl);
// 可多次調(diào)用模板
elem.innerHTML = render(data1);
elem.innerHTML = render(data2);
...

二、避免未定義的變量引起系統(tǒng)崩潰。
若模板中定義了一個(gè)變量輸出,而且傳入數(shù)據(jù)卻少了這個(gè)項(xiàng)目就會(huì)出現(xiàn)變量未定義的錯(cuò)誤,從而引起整個(gè)程序的崩潰。如果無(wú)法確保數(shù)據(jù)完整性,仍然有方法可以對(duì)對(duì)其成員進(jìn)行探測(cè)。原版中暗含變量保存了原始傳入的數(shù)據(jù),即 obj ;而在我的升級(jí)版本中則是關(guān)鍵字 this,如:
復(fù)制代碼 代碼如下:

<% if (this.dataName !== undefined) { %>
<%=dataName %>
<% } %>

三、調(diào)試模板。
由于模板引擎是用文本的調(diào)用的 javascript 引擎,調(diào)試工具無(wú)法定位到出錯(cuò)的行。在 升級(jí)版本 中你可以用調(diào)試工具輸出編譯好的模板緩存。例如調(diào)試這個(gè)模板:
復(fù)制代碼 代碼如下:

<script id="tmpl" type="text/tmpl">
<ul>
<% for (var i = 0, l = list.length; i < l; i ++) { %>
<li><%=list[i].index%>. 用戶: <%=list[i].user%>; 網(wǎng)站:<%=list[i].site%></li>
<% } %>
</ul>

輸出緩存:
復(fù)制代碼 代碼如下:

window.console(tmpl('tmpl').$);

日志結(jié)果:
復(fù)制代碼 代碼如下:

"$1318348744541.push('
<ul> '); for (var i = 0, l = list.length; i < l; i ++) { $1318348744541.push('
<li>',list[i].index,'. 用戶: ',list[i].user,'; 網(wǎng)站:',list[i].site,'</li>
'); } $1318348744541.push(' </ul>
');return $1318348744541"

現(xiàn)在你可以看到模板引擎編譯好的javascript語(yǔ)句,可以對(duì)照這檢查模板是否存在錯(cuò)誤。($1318348744541是一個(gè)隨機(jī)名稱的臨時(shí)數(shù)組,可忽略)
最后非常感謝 tmpl 原作者 與 YayaTemplate 作者的付出,正因?yàn)榇宋也庞袡C(jī)會(huì)深入分析實(shí)現(xiàn)機(jī)制,解決問(wèn)題并從中受益。獨(dú)樂(lè)不如眾樂(lè),分享之。
唐斌 – 2011.10.09 – 湖南-長(zhǎng)沙

相關(guān)文章

最新評(píng)論