Web最佳實(shí)踐:JQuery高性能優(yōu)化
使用***選擇器
使用JQuery時(shí),你可以使用多種選擇器,選擇同一個(gè)元素,各種方法之間的性能是不一樣的,有時(shí)候差異會(huì)特別大。 通常比較常用的選擇器有以下幾個(gè):
-
ID選擇器 $("#id")
-
標(biāo)簽選擇器 $("td")
-
類選擇器 $(".target")
-
屬性選擇器 $("td[target='target']")
-
偽類選擇器 $("td:hidden")
根據(jù)經(jīng)驗(yàn),我們應(yīng)該知道這5種選擇器的性能是依次下降的,我們不妨來做個(gè)測(cè)試,看看他們的性能到底有多大差異:
-
測(cè)試html片段:
- <table width="98%" cellspacing="1" cellpadding="0" border="0" style="table-layout:fixed" id="mytable">
- <tr>
- <td bgcolor="#aaaaaa" align="center" class="target" target="target" style="display:none;" id="target-td">e</td>
- </tr>
- </table>
測(cè)試結(jié)果
-
測(cè)試方案:對(duì)每個(gè)腳本執(zhí)行1w次,統(tǒng)計(jì)3次運(yùn)行結(jié)果的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
$("#mytable td.target") | 5150 | 5630 | 780 | 293 | 69 | 148 | 31 | 102 |
$("#mytable .target") | 5320 | 5780 | 940 | 297 | 61 | 141 | 32 | 101 |
$("#mytable").find("td.target") | 4840 | 5000 | 1250 | 387 | 95 | 205 | 73 | 157 |
$("#mytable").find(".target") | 5000 | 5150 | 1400 | 226 | 49 | 130 | 60 | 64 |
$("#mytable td[target=target]") | 16410 | 17660 | 940 | 406 | 89 | 166 | 35 | 120 |
$("#mytable td:hidden") | 25000 | 26720 | 23750 | 3638 | 632 | 1123 | 3434 | 569 |
$("#target-td") | 630 | 620 | 310 | 62 | 9 | 28 | 12 | 18 |
$(".target") | 10310 | 10790 | 940 | 207 | 36 | 181 | 47 | 44 |
document.getElementById("target-td") | 150 | 150 | 160 | 6 | 1 | 1 | 5 | 2 |
結(jié)論
-
原生方法是最快的方法,如果可能,盡量選擇用原生
-
性能順序:ID選擇器 > 標(biāo)簽選擇器 > 類選擇器 > 屬性選擇器 > 偽類選擇器
-
ID(getElementById)、標(biāo)簽選擇器(getElementsByTagName)都有原生的方法對(duì)應(yīng),所以很快;類選擇器在除了IE5-IE8之外的主流瀏覽器幾乎都有原生方法(getElementsByClassName)
-
為了兼顧IE6、7、8的性能,避免使用全局的類選擇器;
-
屬性和偽類選擇器非常慢,如非必要,盡量少使用偽類選擇器和屬性選擇器
***實(shí)踐
-
為模塊中操作最頻繁的元素和根元素設(shè)置id,然后緩存;
-
對(duì)沒有id的元素檢索,盡量采用路徑最短的祖先元素id作為默認(rèn)的搜索范圍,并采用純類選擇器或者標(biāo)簽選擇器;
-
盡量避免復(fù)雜的選擇器
避免執(zhí)行全局查找
- $("div.bizHelp");
=>
- $("#container").find(".bizHelp");
保證查詢的路徑最短,性能***,參照***條;
避免對(duì)空結(jié)果進(jìn)行操作
對(duì)于數(shù)量為0的選擇結(jié)果,JQuery會(huì)執(zhí)行默認(rèn)動(dòng)作,并且不會(huì)報(bào)錯(cuò),這會(huì)影響程序的性能。
- var blank=$(".blank");//length=0
A :
- blank.slideUp();
B:
- blank.length && blank.slideUp();
測(cè)試結(jié)果
-
測(cè)試說明:1w次執(zhí)行耗時(shí),單位毫秒/ms,統(tǒng)計(jì)三次運(yùn)行的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
A | 6110 | 5610 | 1344 | 488 | 103 | 194 | 108 | 155 |
B | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
結(jié)論
應(yīng)該避免對(duì)空對(duì)象進(jìn)行操作;
采用樣式表,避免多次調(diào)整樣式
對(duì)一個(gè)對(duì)象應(yīng)用多個(gè)樣式,***采用樣式表的方式,避免多次應(yīng)用。
- var obj=$("#obj");
-
A:
- obj.css("width",200);
- obj.css("height",200);
- obj.css("background":"#eee");
-
B:
- obj.attr("style","width:200px;height:200px;background:#eee;");
-
C:
- .css-operation{width:200px;height:200px;background:#eee;}
- obj.addClass("css-operation")
測(cè)試結(jié)果
-
測(cè)試說明:1w次執(zhí)行耗時(shí),單位毫秒/ms,統(tǒng)計(jì)三次運(yùn)行的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
A | 2594 | 2486 | 1500 | 501 | 163 | 222 | 190 | 191 |
B | 1000 | 953 | 547 | 190 | 79 | 28 | 15 | 86 |
C | 843 | 672 | 407 | 111 | 21 | 17 | 16 | 31 |
結(jié)論
-
性能排序:C>B>A
-
樣式和JS分離的方案性能***,適用于要同時(shí)設(shè)置多個(gè)樣式的場(chǎng)景;
-
如果只應(yīng)用單個(gè)樣式,簡(jiǎn)單起見可以考慮采用方案A
-
如果應(yīng)多若干個(gè)樣式,而且不愿意新建一個(gè)css class,可以采用B;
#p#
避免使用匿名函數(shù)
大量的使用匿名函數(shù)會(huì)對(duì)程序的調(diào)試、維護(hù)以及通過第三方軟件來做性能測(cè)試增加難度,因此應(yīng)該盡量避免大量的使用匿名函數(shù)
- obj.click(function(){
- //do something...
- })
=>>
- var clickHandler=function(){
- //do something...
- }
- obj.click(clickHandler)
大循環(huán)采用更高效的遍歷方式
JQuery提供了$.fn.each()和$.each()兩個(gè)方法來實(shí)現(xiàn)對(duì)集合的遍歷,除此之外,還可以采用JS原生的for循環(huán)、while等來實(shí)現(xiàn)迭代,應(yīng)該了解一下他們之間的性能差異:
- var list=ul.find("li"),e;
-
A:
- var i=list.length;
- while(i--){
- e=$(list[i])
- }
-
B:
- list.each(function(){
- e=$(this);
- });
-
C:
- $.each(list,function(){
- e=$(this);
- });
測(cè)試結(jié)果
-
測(cè)試說明:1w次執(zhí)行耗時(shí),單位毫秒/ms,統(tǒng)計(jì)三次運(yùn)行的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
A | 172 | 219 | 157 | 30 | 3 | 5 | 4 | 6 |
B | 219 | 234 | 203 | 41 | 4 | 6 | 5 | 8 |
C | 219 | 234 | 187 | 52 | 3 | 4 | 5 | 7 |
結(jié)論
-
總體上來說A>C>B
-
方案A有大約25%的性能提升,但是不穩(wěn)定;
-
在IE瀏覽器下B方案和C方案性能相當(dāng),A方案有比較絕對(duì)的優(yōu)勢(shì);
-
Chrome、firefox下A方案的性能不穩(wěn)定;
***實(shí)踐
-
追求***性能,用方案A;
-
循環(huán)數(shù)量少的話,建議使用方案C,比較穩(wěn)定;
優(yōu)先使用原生屬性
很多常用的屬性,比如id、name等都被瀏覽器原生實(shí)現(xiàn),在JQuery中我們有時(shí)會(huì)用$(this).attr("id")的方式來獲取id,這種方法的效率相比原生屬性的獲取效率而言,非常慢。
- $.each(list,function(){
- //A
- var id=$(this).attr("id");
- //B
- var id=this.id;
- })
測(cè)試結(jié)果
-
測(cè)試說明:10w次執(zhí)行耗時(shí),單位毫秒/ms,統(tǒng)計(jì)三次運(yùn)行的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
A | 6880 | 7030 | 4220 | 1188 | 157 | 244 | 133 | 135 |
B | 310 | 310 | 150 | 27 | 4 | 5 | 17 | 3 |
結(jié)論
-
使用原生的API,可以極大的提高性能
***實(shí)踐
-
對(duì)于id等常用的屬性,用原生的屬性,不要通過attr去獲?。?/p>
使用事件委托
經(jīng)常會(huì)遇到給一個(gè)列表中所有元素添加點(diǎn)擊事件的業(yè)務(wù)場(chǎng)景,傳統(tǒng)的做法是得到這個(gè)列表的JQuery對(duì)象:$("li"),然后添加click事件:
- $("li").click(function(){})
這種方法的在列表數(shù)量比較大的時(shí)候會(huì)有嚴(yán)重的性能問題,應(yīng)該值得關(guān)注。JQuery在很早的版本中已經(jīng)引入了事件委托機(jī)制,可以很大程度的降低添加事件監(jiān)聽的消耗和內(nèi)存的消耗。
對(duì)1w條記錄的列表進(jìn)行測(cè)試:
-
A:
- var list=$("li");//length>1
- list.click(function(){
- })
-
B:
- $("ul").delegate("li","click",function(){})
測(cè)試結(jié)果
-
測(cè)試說明:對(duì)1w個(gè)<li>標(biāo)簽進(jìn)行click事件添加的耗時(shí),單位毫秒/ms,統(tǒng)計(jì)三次運(yùn)行的平均值
方案 | IE6 | IE7 | IE8 | IE9 | chrome | firefox | opera | safari |
A | 2156 | 2172 | 1922 | 312 | 103 | 173 | 141 | 117 |
B | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
結(jié)論
-
委托的性能優(yōu)勢(shì)是非常絕對(duì)的;
***實(shí)踐
-
對(duì)于需要同時(shí)給兩個(gè)以上的同類型元素添加事件時(shí),用方案B來代替A
#p#
緩存查找的中間結(jié)果
- $(".list-item").show();
- $(".list-item").hide();
=>
- var listItem=$(".list-item");
- listItem.show();
- listItem.hide();
減少DOM操作,盡量批量更新
Dom操作是瀏覽器操作中最為耗時(shí)的操作之一,JQuery中提供了append、appendTo、prepend、prependTo、after、before、insertAfter、wrap等操作dom的實(shí)用方法,頻繁使用這些方法可能會(huì)引起性能問題,一個(gè)提高性能的實(shí)踐原則就是“盡可能少的使用它們”。 如果一定要用到,也盡可能的采用合并、批量操作來減少dom的操作消耗。
使用$.data 而不是$.fn.data
- $(elem).data(key,value);
- $.data(elem,key,value);
后者比前者快近10倍
可能的話,使用***版本的JQuery
新版本總會(huì)對(duì)性能進(jìn)行改進(jìn),還會(huì)提供一些非常好用的工具,如果可以的話,應(yīng)該盡量選用***的版本;
jQuery html性能大坑
jQuery的html方法的作用是為dom元素設(shè)置innerHTML,分析html的源代碼(1.8.3)
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName( "*" ) );
- elem.innerHTML = value;
- }
在設(shè)置dom的innerHTML之前,會(huì)執(zhí)行jQuery.cleanData,這個(gè)方法會(huì)對(duì)dom元素做一些clean的處理,如removeEvent,刪除緩存等。
以兩百行的列表為例,在ff瀏覽器中,該方法會(huì)執(zhí)行大約5ms到8ms。即當(dāng)dom元素為空時(shí)和dom元素中有兩百行數(shù)據(jù)時(shí),執(zhí)行html方法,后者會(huì)比前者多運(yùn)行5ms到8ms。
坑點(diǎn)
cleanData方法在jQueryUI中也會(huì)定義,且會(huì)重寫$.cleanData,增加一些額外的操作,性能會(huì)受到影響。
還是以兩百行的列表為例,在ff瀏覽器中,該方法會(huì)執(zhí)行大約60ms到70ms。即當(dāng)dom元素為空時(shí)和dom元素中有兩百行數(shù)據(jù)時(shí),執(zhí)行html方法,后者會(huì)比前者多運(yùn)行60ms到70ms。
解決方案
1. 采用原生的dom.innerHTML
2.在執(zhí)行html()方法之前,先執(zhí)行remove()方法