欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一次失敗的jQuery優(yōu)化嘗試小結(jié)

 更新時(shí)間:2011年02月06日 16:12:44   作者:  
我經(jīng)常抱怨jQuery的DOM操作性能并不優(yōu)秀,并且經(jīng)常嘗試用一些方法去進(jìn)行優(yōu)化,但是越是優(yōu)化,越是沮喪地發(fā)現(xiàn)jQuery其實(shí)已經(jīng)做得很好,從使用者的角度能夠進(jìn)行的優(yōu)化實(shí)在有限
(這并不意味著jQuery的性能是優(yōu)秀的, 反之只能說(shuō)它是一個(gè)相對(duì)封閉的庫(kù),無(wú)法從外部介入進(jìn)行優(yōu)化)。這篇文章就記錄一次失敗的優(yōu)化經(jīng)歷。
優(yōu)化思想
這一次優(yōu)化的思想來(lái)自于數(shù)據(jù)庫(kù)。在數(shù)據(jù)庫(kù)優(yōu)化的時(shí)候,我們常會(huì)說(shuō)“將大量的操作放在一個(gè)事務(wù)中一起提交,能有效提高效率”。雖然對(duì)數(shù)據(jù)庫(kù)不了解的我并不知道其原因,但是“事務(wù)”的思想?yún)s為我指明了方向(雖然是錯(cuò)的……)。
因此我嘗試將“事務(wù)”這一概念引入到j(luò)Query中,通過(guò)“打開”和“提交”事務(wù),從外部對(duì)jQuery進(jìn)行一些優(yōu)化,其最重要的在于減少each函數(shù)的循環(huán)次數(shù)。
眾所周知,jQuery的DOM操作,以get all, set first為標(biāo)準(zhǔn),其中用于設(shè)置DOM屬性/樣式的操作,幾乎都是對(duì)選擇出來(lái)的元素的一次遍歷,jQuery.access函數(shù)就是其中最核心的部分,其中用于循環(huán)的代碼如下:
復(fù)制代碼 代碼如下:

// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn(
elems[i],
key,
exec ? value.call(elems[i], i, fn(elems[i], key)) : value,
pass
);
}
return elems;
}

比如jQuery.fn.css函數(shù)就是這樣的:
復(fù)制代碼 代碼如下:

jQuery.fn.css = function( name, value ) {
// Setting 'undefined' is a no-op
if ( arguments.length === 2 && value === undefined ) {
return this;
}
return jQuery.access( this, name, value, true, function( elem, name, value ) {
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
});
};

因此,下面這樣的代碼,假設(shè)被選擇的div元素有5000個(gè),則要循環(huán)訪問(wèn)10000個(gè)節(jié)點(diǎn):
jQuery('div').css('height', 300).css('width', 200);
而在我的想法中,在一個(gè)“事務(wù)”中,可以如數(shù)據(jù)庫(kù)的操作一般,通過(guò)保存所有的操作,在“提交事務(wù)”的時(shí)候統(tǒng)一進(jìn)行,將10000次節(jié)點(diǎn)訪問(wèn),減少至5000次,相當(dāng)于提升了“1倍”的性能。
簡(jiǎn)單實(shí)現(xiàn)
“事務(wù)”式的jQuery操作中,提供2個(gè)函數(shù):
begin:打開一個(gè)“事務(wù)”,返回一個(gè)事務(wù)的對(duì)象。該對(duì)象擁有jQuery的所有函數(shù),但是調(diào)用函數(shù)并不會(huì)立刻生效,只有在“提交事務(wù)”后才會(huì)生效。
commit:提交一個(gè)“事務(wù)”,保證所有事先調(diào)用過(guò)的函數(shù)都生效,交返回原本的jQuery對(duì)象。
實(shí)現(xiàn)起來(lái)也很方便:
創(chuàng)建一個(gè)“事務(wù)對(duì)象”,復(fù)制jQuery.fn上所有函數(shù)到該對(duì)象中。
當(dāng)調(diào)用某個(gè)函數(shù)時(shí),在預(yù)先準(zhǔn)備好的“隊(duì)列”中添加調(diào)用的函數(shù)名和相關(guān)參數(shù)。
當(dāng)提交事務(wù)時(shí),對(duì)被選擇的元素進(jìn)行一次遍歷,對(duì)遍歷中的每個(gè)節(jié)點(diǎn)應(yīng)用“隊(duì)列”中的所有函數(shù)。
簡(jiǎn)單地代碼如下:
復(fù)制代碼 代碼如下:

var slice = Array.prototype.slice;
jQuery.fn.begin = function() {
var proxy = {
_core: c,
_queue: []
},
key,
func;
//復(fù)制jQuery.fn上的函數(shù)
for (key in jQuery.fn) {
func = jQuery.fn[key];
if (typeof func == 'function') {
//這里會(huì)因?yàn)閒or循環(huán)產(chǎn)生key始終是最后一個(gè)循環(huán)值的問(wèn)題
//因此必須使用一個(gè)閉包保證key的有效性(LIFT效應(yīng))
(function(key) {
proxy[key] = function() {
//將函數(shù)調(diào)用放到隊(duì)列中
this._queue.push([key, slice.call(arguments, 0)]);
return this;
};
})(key);
}
}
//避免commit函數(shù)也被攔截
proxy.commit = jQuery.fn.commit;
return proxy;
};
jQuery.fn.commit = function() {
var core = this._core,
queue = this._queue;
//僅一個(gè)each循環(huán)
core.each(function() {
var i = 0,
item,
jq = jQuery(this);
//調(diào)用所有函數(shù)
for (; item = queue[i]; i++) {
jq[item[0]].apply(jq, item[1]);
}
});
return this.c;
};

測(cè)試環(huán)境
測(cè)試使用以下條件:
5000個(gè)div放在一個(gè)容器(<div id="container"></div>)中。
使用$('#container>div')選擇這5000個(gè)div。
每個(gè)div要求設(shè)置一個(gè)隨機(jī)背景色(randomColor函數(shù)),和800px以下的隨機(jī)寬度(randomWidth函數(shù))。
參加測(cè)試的調(diào)用方法有3個(gè):
正常使用法:
復(fù)制代碼 代碼如下:

$('#container>div')
.css('background-color', randomColor)
.css('width', randomWidth);

單次循環(huán)法:
復(fù)制代碼 代碼如下:

$('#container>div').each(function() {
$(this).css('background-color', randomColor).css('width', randomWidth);
});

事務(wù)法:
復(fù)制代碼 代碼如下:

$('#container>div')
.begin()
.css('background-color', randomColor)
.css('width', randomWidth)
.commit();

對(duì)象賦值法:
復(fù)制代碼 代碼如下:

$('#container>div').css({
'background-color': randomColor,
'width': randomWidth
});

測(cè)試瀏覽器選擇Chrome 8系列(用IE測(cè)就直接掛了)。
悲傷的結(jié)果
原本的預(yù)測(cè)結(jié)果是,單次循環(huán)法的效率遠(yuǎn)高于正常使用法,同時(shí)事務(wù)法雖然比單次循環(huán)法慢一些,但應(yīng)該比正常使用法更快,而對(duì)象賦值法其實(shí)是jQuery內(nèi)部支持的單次循環(huán)法,效率應(yīng)該是最高的。
然而遺憾的是,結(jié)果如下:
正常使用法 單次循環(huán)法 事務(wù)法 對(duì)象賦值法
18435ms 18233ms 18918ms 17748ms
從結(jié)果上看,事務(wù)法成了最慢的一種方法。同時(shí)單次循環(huán)與正常使用并沒(méi)有明顯的優(yōu)勢(shì),甚至依賴jQuery內(nèi)部實(shí)現(xiàn)的對(duì)象賦值法也沒(méi)有拉開很大的差距。
由于5000個(gè)元素的操作已經(jīng)是非常龐大的循環(huán),如此龐大的循環(huán)也沒(méi)能拉開性能的差距,平時(shí)最常用的10個(gè)左右的元素操作更不可能有明顯的優(yōu)勢(shì),甚至還可能將劣勢(shì)擴(kuò)大化。
究其原因,由于本身單次循環(huán)法就沒(méi)有明顯的性能提升,因此依賴于單次循環(huán),并是在單次循環(huán)之上進(jìn)行外部構(gòu)建的事務(wù)法,自然是在單次循環(huán)的基礎(chǔ)上還需要額外增加創(chuàng)建事務(wù)對(duì)象、保存函數(shù)隊(duì)列、遍歷函數(shù)隊(duì)列等開銷,結(jié)果敗給正常使用法也在情理之中。
至此,也算是可以宣布模仿“事務(wù)”的優(yōu)化之道的失敗。但是對(duì)這個(gè)結(jié)果卻還能進(jìn)一步地分析。
性能在哪里
首先,從代碼的使用上來(lái)分析,將正常使用法和測(cè)試中最快的對(duì)象賦值法進(jìn)行比較,可以說(shuō)兩者的差距僅在于循環(huán)的元素個(gè)數(shù)的不同(這里拋開了jQuery的內(nèi)部問(wèn)題,事實(shí)上jQuery.access的糟糕實(shí)現(xiàn)也確實(shí)有拖對(duì)象賦值法后腿之嫌,好在并不嚴(yán)重),正常使用法是10000個(gè)元素,對(duì)象賦值法是5000個(gè)元素。因此可以簡(jiǎn)單地認(rèn)為,18435 - 17748 = 687ms是循環(huán)5000個(gè)元素的耗時(shí),這占到整個(gè)執(zhí)行過(guò)程的3.5%左右,并不是整個(gè)執(zhí)行過(guò)程的主干,其實(shí)真的沒(méi)有優(yōu)化的必要。

那么另外96.5%的開銷去了哪里呢?謹(jǐn)記Doglas的一句話,“事實(shí)上Javascript并不慢,慢的是DOM的操作”。其實(shí)剩下96.5%的開銷中,除去函數(shù)調(diào)用等基本的消耗,至少有95%的時(shí)間是用在了DOM元素的樣式被改變后的重新渲染之上。

發(fā)現(xiàn)了這個(gè)事實(shí)之后,其實(shí)也就有了更正確的優(yōu)化方向,也是前端性能中的基本原則之一:在修改大量子元素時(shí),先將根父DOM節(jié)點(diǎn)移出DOM樹。因此如果使用以下的代碼再進(jìn)行測(cè)試:
復(fù)制代碼 代碼如下:

//沒(méi)有重用$('#container')已經(jīng)很糟糕了
$('#container').detach().find('div')
.css('background-color', randomColor)
.css('width', randomWidth);
$('#container').appendTo(document.body);

測(cè)試結(jié)果始終停留在900ms左右,與前面的數(shù)據(jù)完全不在一個(gè)數(shù)量級(jí)之上,真正的優(yōu)化成功。

教訓(xùn)和總結(jié)
千萬(wàn)要找到正確的性能瓶頸再進(jìn)行優(yōu)化,盲目的猜測(cè)只會(huì)導(dǎo)致走上錯(cuò)誤而偏激的道路。
數(shù)據(jù)說(shuō)話,數(shù)據(jù)面前誰(shuí)也別說(shuō)話!
不認(rèn)為“事務(wù)”這個(gè)方向是錯(cuò)誤的,如果jQuery原生就能支持“事務(wù)”這樣的概念,會(huì)不會(huì)有其他的點(diǎn)可以優(yōu)化?比如一個(gè)事務(wù)自動(dòng)會(huì)將父元素脫離出DOM樹之類的……

相關(guān)文章

  • jQuery ajax dataType值為text json探索分享

    jQuery ajax dataType值為text json探索分享

    ajax dataType值為text json的使用是怎么樣的,在本文將為大家想你想介紹下,感興趣的朋友不要錯(cuò)過(guò)
    2013-09-09
  • 最新評(píng)論