書寫高質量jQuery程式碼的12條經驗

Dunizb發表於2016-07-15

1、正確引用jQuery

  1. 儘量在body結束前才引入jQuery,而不是在head中。

  2. 藉助第三方提供的CDN來引入jQuery,同時注意當使用第三方CDN出現問題時,要引入本地的jQuery檔案。

  3. 如果在</body>前引入script檔案的話,就不用寫document.ready了,因為這時執行js程式碼時DOM已經載入完畢了。

<body>  
    <script src="http://lib.sinaapp.com/js/jquery11/1.8/jquery.min.js"></script>  
    <script>window.jQuery || document.write(`<script src="jquery1.8.min.js">x3C/script>`)</script>  
</body> 

2、優化jQuery選擇器

高效正確的使用jQuery選擇器是熟練使用jQuery的基礎,而掌握jQuery選擇器需要一定的時間積累,我們開始學習jQuery時就應該注意選擇器的使用。

<div id="nav" class="nav">  
    <a class="home" href="http://www.jquery001.com">jQuery學習網</a>  
    <a class="articles" href="http://www.jquery001.com/articles/">jQuery教程</a>  
</div>

如果我們選擇class為home的a元素時,可以使用下邊程式碼:

$(`.home`);  //1  
$(`#nav a.home`);  //2  
$(`#nav`).find(`a.home`);  //3 

方法1,會使jQuery在整個DOM中查詢class為home的a元素,效能可想而知。
方法2,為要查詢的元素新增了上下文,在這裡變為查詢id為nav的子元素,查詢效能得到了很大提升。
方法3,使用了find方法,它的速度更快,所以方法三最好。
關於jQuery選擇器的效能優先順序,ID選擇器快於元素選擇器,元素選擇器快於class選擇器。因為ID選擇器和元素選擇器是原生的JavaScript操作,而類選擇器不是,大家順便可以看下find context 區別,find() children區別。

2.1、一些規則

  • CSS解析引擎將自右向左計算每一條規則,它從關鍵選擇器開始,自右向左計算每一個選擇器,直到發現一個匹配的選擇器,如果沒有找到匹配的選擇器則放棄查詢。

  • 使用較低層的規則通常更有效率。

  • 儘可能的具體化的選擇器——ID要比tag更好。

  • 避免不必要的冗餘。

通常請情況下,請保持選擇器簡單明瞭(比如充分使用ID選擇器),儘可能的使用關鍵選擇器更具體,無論對JavaScript還是CSS,這都可以加塊網站的速度。到目前為止,無論使用哪一種瀏覽器,使用ID選擇器和當個類選擇器都是選中元素最快的方式。

2.2、避免多個ID選擇符

Id選擇符應該是唯一的,所以沒有必要新增額外的選擇符。

// 糟糕
$(`div#myid`);
$(`div#footer a.myLink`);
// 建議
$(`#myid`);
$(`#footer .myLink`);

在此強調,ID 選擇符應該是唯一的,不需要新增額外的選擇符,更不需要多個後代ID選擇符。

// 糟糕
$(`#outer #inner`);
// 建議
$(`#inner`);

2.3、避免隱式通用選擇符

通用選擇符有時是隱式的,不容易發現。

// 糟糕
$(`.someclass :radio`);
// 建議
$(`.someclass input:radio`);

2.4、避免通用選擇符

將通用選擇符放到後代選擇符中,效能非常糟糕。

// 糟糕
$(`.container > *`);
// 建議
$(`.container`).children();

2.5、選擇捷徑

精簡程式碼的其中一種方式是利用編碼捷徑。

// 糟糕
if(collection.length > 0){..}
// 建議
if(collection.length){..}

2.6、為選擇器指定上下文

預設情況下,當把一個選擇器傳遞給jQuery時,它將遍歷整個DOM,jQuery方法還具有一個未充分利用的引數,既可以將一個上下文引數傳入jQuery,以限制它只搜尋DOM中特定的一部分。

//糟糕,會遍歷整個DOM
$(".class");
//建議,只搜尋#id元素下的class,id指明搜尋範圍
$(".class","#id");

jQuery選擇器的效能比較:

$(".class","#id") > $("#id .class") > $(".class")

3、快取jQuery物件

快取jQuery物件可以減少不必要的DOM查詢,關於這點大家可以參考下快取jQuery物件來提高效能。

// 糟糕
h = $(`#element`).height();
$(`#element`).css(`height`,h-20);
// 建議
$element = $(`#element`);
h = $element.height();
$element.css(`height`,h-20);

3.1、使用子查詢快取的父元素

正如前面所提到的,DOM遍歷是一項昂貴的操作。典型做法是快取父元素並在選擇子元素時重用這些快取元素。

// 糟糕
var
    $container = $(`#container`),
    $containerLi = $(`#container li`),
    $containerLiSpan = $(`#container li span`);
// 建議 (高效)
var
    $container = $(`#container `),
    $containerLi = $container.find(`li`),
    $containerLiSpan= $containerLi.find(`span`);

4、變數

4.1、避免全域性變數

jQuery與javascript一樣,一般來說,最好確保你的變數在函式作用域內。

// 糟糕
$element = $(`#element`);
h = $element.height();
$element.css(`height`,h-20);
// 建議
var $element = $(`#element`);
var h = $element.height();
$element.css(`height`,h-20);

4.2、使用匈牙利命名法

在變數前加$字首,便於識別出jQuery物件。

// 糟糕
var first = $(`#first`);
var second = $(`#second`);
var value = $first.val();
// 建議 - 在jQuery物件前加$字首
var $first = $(`#first`);
var $second = $(`#second`),
var value = $first.val();

4.3、使用 Var 鏈(單 Var 模式)

將多條var語句合併為一條語句,我建議將未賦值的變數放到後面。

var $first = $(`#first`),
      $second = $(`#second`),
      value = $first.val(),
      k = 3,
      cookiestring = `SOMECOOKIESPLEASE`,
      i,
      j,
      myArray = {};

5、正確使用事件委託

在新版jQuery中,更短的 on(“click”) 用來取代類似 click() 這樣的函式。在之前的版本中 on() 就是 bind()。自從jQuery 1.7版本後,on() 附加事件處理程式的首選方法。然而,出於一致性考慮,你可以簡單的全部使用 on()方法。

<table id="t">  
    <tr>  
        <td>我是單元格</td>  
    </tr>  
</table>  

比如我們要在上邊的單元格上繫結一個單擊事件,不注意的朋友可能隨手寫成下邊的樣子:

$(`#t`).find(`td`).on(`click`, function () {  
    $(this).css({ `color`: `red`, `background`: `yellow` });  
});

這樣會為每個td綁上事件,在為100個單元格繫結click事件的測試中,兩者效能相差7倍之多,好的做法應該是下邊寫法:

$(`#t`).on(`click`, `td`, function () {  
    $(this).css({ `color`: `red`, `background`: `yellow` });  
}); 

6、精簡jQuery程式碼

如在上述程式碼中我們對jQuery程式碼進行了適當的合併,類似的還有.attr()方法等,我們沒有寫成下邊的方式:

$(`#t`).on(`click`, `td`, function () {  
    $(this).css(`color`, `red`).css(`background`, `yellow`);  
});

6.1、合併函式

一般來說,最好儘可能合併函式。

// 糟糕
$first.click(function(){
    $first.css(`border`,`1px solid red`);
    $first.css(`color`,`blue`);
});
// 建議
$first.on(`click`,function(){
    $first.css({
        `border`:`1px solid red`,
        `color`:`blue`
    });
});

6.2、鏈式操作

jQuery實現方法的鏈式操作是非常容易的。下面利用這一點。

// 糟糕
$second.html(value);
$second.on(`click`,function(){
    alert(`hello everybody`);
});
$second.fadeIn(`slow`);
$second.animate({height:`120px`},500);
// 建議
$second.html(value);
$second.on(`click`,function(){
    alert(`hello everybody`);
}).fadeIn(`slow`).animate({height:`120px`},500);

7、減少DOM操作

剛開始使用jQuery時可能會頻繁的操作DOM,這是相當耗費效能的。如我們要在body中動態輸出一個表格,一些朋友會這樣寫:

//糟糕
var $t = $(`body`);  
$t.append(`<table>`);  
$t.append(`<tr><td>1</td></tr>`);  
$t.append(`</table>`);
//建議
$(`body`).append(`<table><tr><td>1</td></tr></table>`);

這樣在拼接完table串後再新增到body中,對DOM的操作只需一次。群裡以前有朋友就因為這個導致在IE下輸出時出現問題,而關於字串的拼接可以參考下最快建立字串的方法。

7.1、繁重的操作中分離元素

如果你打算對DOM元素做大量操作(連續設定多個屬性或css樣式),建議首先分離元素然後在新增。

// 糟糕
var
    $container = $("#container"),
    $containerLi = $("#container li"),
    $element = null;
$element = $containerLi.first();
//... 許多複雜的操作
// better
var
    $container = $("#container"),
    $containerLi = $container.find("li"),
    $element = null;
$element = $containerLi.first().detach();
//... 許多複雜的操作
$container.append($element);

7.2、最小化DOM更新

重佈局和重繪是WEB頁面中最常見的也是最昂貴的兩種操作。

  • 當改變樣式,而不改變頁面幾何佈局時,將會發生重繪。隱藏一個元素或者改變一個元素的背景色時都將導致一次重繪。

  • 當對頁面結構進行更新時,將導致頁面重佈局。

//糟糕
for(var i=0; i<10000; i++){
    $("#main table").append("<tr><td>aaaa</td></tr>");
}

//建議
var tablerow = "";
for(var i=0; i<10000; i++){
    tablerow  += "<tr><td>aaaa</td></tr>";
}
$("#main table").append(tablerow);

8、維持程式碼的可讀性

伴隨著精簡程式碼和使用鏈式的同時,可能帶來程式碼的難以閱讀。新增縮緊和換行能起到很好的效果。

// 糟糕
$second.html(value);
$second.on(`click`,function(){
    alert(`hello everybody`);
}).fadeIn(`slow`).animate({height:`120px`},500);
// 建議
$second.html(value);
$second
    .on(`click`,function(){ alert(`hello everybody`);})
    .fadeIn(`slow`)
    .animate({height:`120px`},500);

9、選擇短路求值

短路求值是一個從左到右求值的表示式,用 &&(邏輯與)或 ||(邏輯或)操作符。

// 糟糕
function initVar($myVar) {
    if(!$myVar) {
        $myVar = $(`#selector`);
    }
}
// 建議
function initVar($myVar) {
    $myVar = $myVar || $(`#selector`);
}

10、堅持最新版本

新版本通常更好:更輕量級,更高效。顯然,你需要考慮你要支援的程式碼的相容性。例如,2.0版本不支援ie 6/7/8。
摒棄棄用方法
關注每個新版本的廢棄方法是非常重要的並儘量避免使用這些方法。

// 糟糕 - live 已經廢棄
$(`#stuff`).live(`click`, function() {
  console.log(`hooray`);
});
// 建議
$(`#stuff`).on(`click`, function() {
  console.log(`hooray`);
});
// 注:此處可能不當,應為live能實現實時繫結,delegate或許更合適

11、最後忠告

最後,我記錄這篇文章的目的是提高jQuery的效能和其他一些好的建議。如果你想深入的研究對這個話題你會發現很多樂趣。記住,jQuery並非不可或缺,僅是一種選擇。思考為什麼要使用它。DOM操作?ajax?模版?css動畫?還是選擇符引擎?或許javascript微型框架或jQuery的定製版是更好的選擇。

雖然都是陳詞濫調,但是我發現還不能很好得做到上述所有,記錄下來希望自己能夠全部做到。

12、不使用jQuery

原生函式總是最快的,這點不難理解,在程式碼書寫中我們不應該忘記原生JS。
就先總結這幾條吧,每條建議並不難理解,但總結全面的話還是要花費不少時間的。如在減少程式碼段中,如果需要根據條件從陣列中得到新陣列時,可以使用$.grep() 方法,如果你在使用jQuery時有自己心得的話,歡迎在留言中和大家分享!


參考資料:
《jQuery高階程式設計》[Cesar Otero,Rob Larsen],(譯)施巨集斌,清華大學出版社,2013年4月第1版
http://mp.weixin.qq.com/s?__b…

相關文章