JQuery編碼規(guī)范與最佳實(shí)踐

2018-06-09 18:19 更新
大綱
  1. 1. jquery的加載
    1. 1.1. 盡量使用CDN資源
    2. 1.2. 使用裸協(xié)議的URL
    3. 1.3. 減少頁面阻塞
    4. 1.4. 如何選擇版本
    5. 1.5. 注意不同類庫的變量沖突
    6. 1.6. 瀏覽器器特性檢測
  2. 2. jquery的變量
    1. 2.1. 添加變量前綴
    2. 2.2. 適時緩存變量
    3. 2.3. 使用駝峰式命名法
  3. 3. 選擇器
    1. 3.1. 盡量使用ID選擇器
    2. 3.2. 使用簡單的類選擇器
    3. 3.3. 適時的使用find()
    4. 3.4. 提升多級查找效率
    5. 3.5. 避免冗余選擇器
    6. 3.6. 給定上下文
    7. 3.7. 盡量避免使用模糊選擇器
    8. 3.8. 警惕隱式的模糊選擇器
    9. 3.9. 唯一的ID選擇器
  4. 4. DOM操作
    1. 4.1. 先卸載再操作
    2. 4.2. 減少不必要的元素操作
    3. 4.3. 避免操作不存在的元素
  5. 5. 事件
    1. 5.1. 一個文檔ready處理程序
    2. 5.2. 慎用匿名函數(shù)作為回調(diào)
    3. 5.3. 盡量避免在html中內(nèi)聯(lián)js代碼
    4. 5.4. 事件命名空間
    5. 5.5. 使用事件代理
  6. 6. Ajax異步操作
    1. 6.1. 盡量使用$.ajax()
    2. 6.2. 請求可以省略協(xié)議
    3. 6.3. 正確傳遞參數(shù)
    4. 6.4. Ajax雜項(xiàng)
    5. 6.5. 通用ajax模板
  7. 7. 動畫與特效
  8. 8. 插件
  9. 9. 鏈?zhǔn)秸Z法
  10. 10. 其他
    1. 10.1. 使用對象直接量傳遞參數(shù)
    2. 10.2. 不要將css揉進(jìn)jquery中
    3. 10.3. 最好別用已被官方廢棄的方法
    4. 10.4. 適時的使用原生js
  11. 11. 參考列表

偶然發(fā)現(xiàn)我很少寫jquery相關(guān)的文章。作為一名前端從業(yè)人員,jquery可以說一門必備的技能,能夠熟練使用jquery是每一個前端開發(fā)者應(yīng)該掌握的。不過這篇文章我并不打算寫成從零開始的教程式文章,僅僅闡述一些使用jquery需要注意的一些內(nèi)容。

前幾日有幸讀到一篇文章,講得就是使用jquery的一些周邊知識,包括一些相關(guān)的最佳實(shí)踐,感覺受益匪淺。原文地址在這里,這篇文章應(yīng)該寫了有一段時間了,不過可喜的是,作者并沒有棄之不理,反而在最近有修改過相關(guān)內(nèi)容。所以我決定索性翻譯此篇文章以作備忘(本文的翻譯并非完全按照原文照字照句翻譯的,在某些位置做了內(nèi)容合并以及應(yīng)用了一些活躍的修飾手法,不過不影響原文的含義)。

譯文開始。

jquery的加載

在使用jquery時,加載jquery會有一些需要注意的小技巧,下面聽我一一道來。

盡量使用CDN資源

使用cdn總會有一些益處,而且又不要錢,不用白不用。

不過,萬一你用的cdn在關(guān)鍵時候掉鏈子,那就不好玩了。所以我們還需要整點(diǎn)小技巧來防著這一手。

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js" rel="external nofollow" ></script>
<script>
    window.jQuery || document.write('<script src="js/jquery-2.1.1.min.js" type="text/javascript"><\/script>')
</script>

這里稍微解釋一下。上面代碼中第一個<script>標(biāo)簽的作用比較顯目,就是加載cdn提供的jquery資源。而第二個<script>標(biāo)簽中的代碼就是為了防止cdn掛了而作出的特別處理。其中的javascript代碼使用了短語判斷,若瀏覽器環(huán)境已經(jīng)存在window.jQuery變量則什么也不做;否則動態(tài)的插入一個<script>標(biāo)簽來加載自己服務(wù)器上的jquery資源。

所以,看起來很普通的jq文件加載過程,也是有不少門道的。

我們接著往下看。

使用裸協(xié)議的URL

出現(xiàn)了一個不太常見的詞,裸協(xié)議。啥意思呢?很簡單就是說去掉url中的http:https:,就想前面的代碼塊中一樣。關(guān)于裸協(xié)議的更多解釋可以參閱這里。

譯者注:經(jīng)過譯者多方資料的查閱,發(fā)現(xiàn)這一點(diǎn)不再是一個推薦的做法。如果你的頁面使用的是https協(xié)議(需要SSL),那么你在html中引用外部cdn資源時,最好還是帶上https://協(xié)議。

減少頁面阻塞

盡可能的將你的js代碼和jquery引用代碼放在頁面的底部。為什么應(yīng)該這么做呢?簡單來說,如果js代碼塊在html的上部的話,就會阻塞頁面主體內(nèi)容的加載。一般我們都是將js代碼塊,包括js代碼的引用代碼都是放在</body>標(biāo)簽之前。

關(guān)于這一點(diǎn),原文給出了一篇參閱文章。此外,開源屆還有一個非常流行的項(xiàng)目,html5-boilerplate是一個非常專業(yè)的前端起手模板,可以多參考參考。

如何選擇版本

眾所周知,jquery有很多的版本,特別地,jquery1.x.x版本和jquery2.x.x版本存在著非常大的差異。那么我們實(shí)際在生產(chǎn)環(huán)境中針對如此多的不同版本究竟要如何抉擇呢?下面是幾個推薦的考慮要素,

  • 如果你必須要支持IE6、7、8,那么不好意思,最新版的jquery2.x.x跟你無緣咯。
  • 如果你不需要考慮兼容性等因素,那么極力推薦你使用最新版本的jquery。
  • 如果你打算使用cdn提供的jquery資源,請務(wù)必提供完整的版本,比如1.9.2。
  • 切記莫要重復(fù)加載引用多個版本的jquery資源。
  • 切記莫要在生產(chǎn)環(huán)境中使用cdn提供的所謂最新本jquery(jquery-latest.js

注意不同類庫的變量沖突

如果你的項(xiàng)目中除了jquery之外,還使用了諸如Prototype,MooTools,Zepto等等,此時你應(yīng)該需要謹(jǐn)慎一點(diǎn),因?yàn)檫@些類庫也使用$作為他們的暴露接口。此時我們可以直接使用jQuery。并且在存在多個類庫接口沖突隱患時,我們應(yīng)該調(diào)用類庫提供的$.noConflict()來避免沖突。

瀏覽器器特性檢測

因?yàn)閖query1.9.x之后的版本中,取消了$browser轉(zhuǎn)而提供一個用于特性檢測的變量$.support,而且jquery官方的言下之意是,這個變量是jquery內(nèi)部使用的,開發(fā)者們最好不要使用,同時推薦開發(fā)者們使用Modernizr來進(jìn)行特性探測。

譯者注:本博客之前也有一篇討論瀏覽器特性探測的文章,若有興趣,請移步這里

jquery的變量

在使用jquery變量時,我們會有一些約定成俗的不成文規(guī)定。

添加變量前綴

往往我們會為了效率或者訪問考慮,臨時緩存一些jquery變量,針對這些變量我們應(yīng)該加上一個$前綴以示區(qū)別。

var $myDiv = $('#myDiv');
$myDiv.click(function() {
    // TODO
});

適時緩存變量

通常我們會通過jquery選擇出一個dom元素,然后對dom元素進(jìn)行各式各樣的操作。這里我們一般建議先將相關(guān)dom元素的jquery對象進(jìn)行緩存,以便后續(xù)的各種操作,同時也有一個提升效率的因素在里面。

使用駝峰式命名法

在前端界,駝峰式命名法基本上得到了所有社區(qū)的認(rèn)可。當(dāng)你需要閱讀別人代碼時,駝峰式的變量命名會讓你感覺更加親切。

選擇器

一般來說,使用jquery進(jìn)行前端開發(fā)的思路都差不多。第一步選擇一個dom元素,第二步對dom元素進(jìn)行各式各樣的操作,包括事件監(jiān)聽,改變dom展示等等。

jquery的核心基本上可以說就是其選擇器。

盡量使用ID選擇器

在可能的情況下,盡量使用ID選擇器。因?yàn)樗旄踩?。(因?yàn)槠鋬?nèi)部就是封裝的原生的document.getElementById(),不會經(jīng)過sizzle查找算法)

使用簡單的類選擇器

有的人在使用class選擇器時,喜歡帶上元素類型。其實(shí)這種做法若非必要,反而會降低選擇器的效率。

var $products = $('div.products'); // 慢
var $products = $('.products');    // 快

適時的使用find()

在ID容器下查找子元素時,推薦使用find()方法,因?yàn)檫@樣更快。其背后的機(jī)制跟前面的盡量使用ID選擇器基本一樣。因?yàn)镮D選擇器不會經(jīng)過sizzle的查找算法。

var ele = $('#products div.id');        // 慢
var ele = $('#products').find('div.id') // 快

提升多級查找效率

在使用jquery進(jìn)行多級dom元素查找時,應(yīng)當(dāng)遵循這樣一個規(guī)則,即左簡右繁。啥意思呢?讓我們來看個例子。

$('div.data .gonzalez');    // 較慢
$('.data td.gonzalez');     // 較快

左簡右繁的含義,簡單來說,盡量左側(cè)的選擇器簡單,而越是靠右側(cè)的選擇器越要詳細(xì)。

再舉個具體點(diǎn)的例子。

<div class="wrapper">
    <div class="left">
        <h2>...</h2>
        <div class="content">
            <p></p>
        </div>
    </div>
    <div class="main"></div>
    <div class="right"></div>
</div>

現(xiàn)在我若想選擇.content下的p標(biāo)簽元素,會有許多方法。這里我們可以這么來,

var $p = $('.wrapper .left.content.p');

它會比下面這種方式要更加效率,

var $p = $('.wrapper.left.content p');

至于這是為什么?我這里不打算過多的闡述,不過可以指出的是,這跟css的解析優(yōu)先級(css會優(yōu)先解析從右側(cè)開始解析)以及jquery的sizzle引擎有關(guān)系。讀者朋友們?nèi)粲信d趣可以參閱原文給出的這篇文章或者自行查閱相關(guān)資料。

避免冗余選擇器

有時候我們在寫選擇器時,會不自覺的嵌套很多層。其實(shí)一葉障目,跳出來后,你可能會發(fā)現(xiàn)有時候會變得很簡單。

$(".data table.attendees td.gonzalez");
$(".data td.gonzalez"); // 省略中間多余的選擇器,提升效率

原文中還給出了簡陋的性能測試來說明這一點(diǎn)。

給定上下文

$('.class'); // bad case. 因?yàn)樾枰兞空麄€dom元素來查找.class元素
$('.class', '#class-container'); // good case. 因?yàn)樗粫闅v整個dom元素,僅僅在父級容器中進(jìn)行查找

盡量避免使用模糊選擇器

因?yàn)槟:x擇器往往會帶來較多的匹配消耗,從而較低了選擇器效率。

$('div.container > *'); // BAD
$('div.container').children(); // BETTER

警惕隱式的模糊選擇器

大多數(shù)時候,省略的選擇器其實(shí)就是*選擇器。

$('div.someclass :radio'); // BAD
$('div.someclass input:radio'); // GOOD

唯一的ID選擇器

經(jīng)常會看到一些jquery新手在使用id選擇器會犯一個毛病,就是混搭id選擇器和其他選擇器。其實(shí)這完全沒有必要。因?yàn)閕d選擇器已經(jīng)表示了唯一性。

$('#outer #inner'); // BAD
$('div#inner'); // BAD
$('.outer-container #inner'); // BAD
$('#inner'); // GOOD, only calls document.getElementById()

DOM操作

先卸載再操作

謹(jǐn)記一條,在對一個dom元素進(jìn)行大量繁雜操作之前,最好將其脫離原先的dom文檔,操作完畢之后再重新append上去。

至于為何要這么做。最核心的原因是防止在進(jìn)行操作dom元素時導(dǎo)致dom樹的頻繁更新,甚至導(dǎo)致頁面的重繪和重排,從而降低了頁面效率。更多可參考這里

var $myList = $("#list-container > ul").detach();
// ... 針對$myList的一系列操作
$myList.appendTo("#list-container");

減少不必要的元素操作

使用jquery時,經(jīng)常會遇到需要使用手動拼接dom元素字符串的情況。這里,應(yīng)該謹(jǐn)記一條,就是應(yīng)該在所有處理操作完畢之后再append到dom樹中,而不是處理一次就append一次。更多可參考這里

// BAD
var $myList = $("#list");
for(var i = 0; i < 10000; i++){
    $myList.append("<li>"+i+"</li>");
}
// GOOD
var $myList = $("#list");
var list = "";
for(var i = 0; i < 10000; i++){
    list += "<li>"+i+"</li>";
}
$myList.html(list);
// EVEN FASTER
// [].join()方法會將所有的數(shù)組元素鏈接成一個字符串,而且速度不俗。
var array = []; 
for(var i = 0; i < 10000; i++){
    array[i] = "<li>"+i+"</li>"; 
}
$myList.html(array.join(''));

原文中還給出一個性能比較。

避免操作不存在的元素

有時候我們通過jquery選擇器拿到的jquery對象是個空數(shù)組([]),可能是那一塊內(nèi)容還未生成或者其他什么原因。此時我們不應(yīng)該對一個空的jquery對象進(jìn)行操作。詳情可參考這里。

// BAD: This runs three functions before it realizes there's nothing in the selection
// jquery需要運(yùn)行3個函數(shù),才會知道選擇器什么也沒拿到。
$("#nosuchthing").slideUp();
// GOOD
var $mySelection = $("#nosuchthing");
if ($mySelection.length) {
    $mySelection.slideUp();
}

事件

一個文檔ready處理程序

一個頁面只寫一個文檔ready事件的處理程序。這樣代碼既清晰好調(diào)試,又容易跟蹤代碼的進(jìn)程。

慎用匿名函數(shù)作為回調(diào)

匿名函數(shù)往往會造成一些不便之處,比如不利于調(diào)試、維護(hù)、測試、復(fù)用等等。更多內(nèi)容可參考這里

$("#myLink").on("click", function(){...}); // BAD
// GOOD
function myLinkClickHandler(){...}
$("#myLink").on("click", myLinkClickHandler);

同時,處理文檔ready的事件回調(diào)函數(shù)也盡量別用匿名函數(shù)。

$(function(){ ... }); // BAD: You can never reuse or write a test for this function.
// GOOD
$(initPage); // or $(document).ready(initPage);
function initPage(){
    // Page load event where you can initialize values and call other initializers.
}

除此之外,處理文檔ready的事件回調(diào)函數(shù)應(yīng)該使用外部文件引入的方式,而且頁面中嵌入初始化調(diào)用即可。

<script src="my-document-ready.js"></script>
<script>
true// Any global variable set-up that might be needed.
true$(document).ready(initPage); // or $(initPage);
</script>

譯者注:關(guān)于這一塊內(nèi)容,譯者是不太贊同原作者的觀點(diǎn)的。

盡量避免在html中內(nèi)聯(lián)js代碼

html允許直接在html標(biāo)簽的屬性中添加js相關(guān)的邏輯處理代碼。這雖然帶來了方便,但是更多的是給調(diào)試帶來了不便。我們應(yīng)該總是走jquery先選擇后綁定這個路子。


<a id="myLink" href="#" onclick="myEventHandler();">my link</a> <!-- BAD -->


$("#myLink").on("click", myEventHandler); // GOOD

事件命名空間

如果可以,我們可以設(shè)計(jì)一套專門用于區(qū)分事件的命名空間。這樣利于維護(hù)且不會對其他事件造成影響。


$("#myLink").on("click.mySpecialClick", myEventHandler); // GOOD
// Later on, it's easier to unbind just your click event
$("#myLink").unbind("click.mySpecialClick");

使用事件代理

當(dāng)你需要給相似的元素添加同一個事件處理時,你就需要用到j(luò)query的事件代理了。所謂事件代理,說簡單點(diǎn)就是給系列元素的父元素添加事件監(jiān)聽,而且事件真正觸發(fā)在其子元素上。


$("#list a").on("click", myClickHandler); // 不好。因?yàn)橄喈?dāng)于添加了多次相同的事件
$("#list").on("click", "a", myClickHandler); // 不錯。使用事件代理,在父元素上添加事件,在子元素上觸發(fā)

Ajax異步操作

jquery內(nèi)置了ajax方法,提供XMLHTTPRequest服務(wù),并且屏蔽了不同瀏覽器對XMLHTTPRequest的差異支持。

盡量使用$.ajax()

有的人喜歡使用$.get(),$.getJson()等方法,不過jquery內(nèi)部任然是將其轉(zhuǎn)換成$.ajax()

譯者注:關(guān)于這一點(diǎn),譯者是不太贊同原作者的觀點(diǎn)的。

請求可以省略協(xié)議

在https站上不要使用http請求,最好是請求時別指定請求協(xié)議。(將http或者h(yuǎn)ttps從你的url中移除)

正確傳遞參數(shù)

不要在請求url中附帶參數(shù)。$.ajax()提供有專門參數(shù)用于傳遞參數(shù)。后者更加可讀。


// Less readable...
$.ajax({
    url: "something.php?param1=test1&param2=test2",
    ....
});
// More readable...
$.ajax({
    url: "something.php",
    data: { param1: test1, param2: test2 }
});

Ajax雜項(xiàng)

發(fā)起ajax請求時,最好都指定請求返回的數(shù)據(jù)類型(dataType)。

使用事件代理,以便ajax動態(tài)加載出來的內(nèi)容仍然能夠觸發(fā)之前的事件綁定。更多內(nèi)容可參考這里。


$("#parent-container").on("click", "a", delegatedClickHandlerForAjax);

使用Promise模式來處理ajax不同情況的返回。點(diǎn)我查看更多的示例。


$.ajax({ ... }).then(successHandler, failureHandler);
// OR
var jqxhr = $.ajax({ ... });
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);

通用ajax模板

下面是一個通用的使用jquery發(fā)送ajax請求的模板。更多的說明可參考這里


var jqxhr = $.ajax({
    url: url,
    type: "GET", // 默認(rèn)是GET,不過使用其他http動詞
    cache: true, // 默認(rèn)是true,不過當(dāng)dataType為script和jsonp時為false
    data: {}, // 請求參數(shù)
    dataType: "json", // 最好指明請求返回的數(shù)據(jù)類型,默認(rèn)為json
    jsonp: "callback", // 指定回調(diào)處理JSONP類型的請求
    statusCode: { // 如果你想處理其他的錯誤,可以在這里指明各錯誤碼對應(yīng)的回調(diào)函數(shù)
        404: handler404,
        500: handler500
    }
});
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);

動畫與特效

下面是兩點(diǎn)關(guān)于jquery動畫的通用建議。

  1. 盡量讓所有的動畫和特效風(fēng)格如一,不要讓人感覺頭暈?zāi)垦?/li>
  2. 切記濫用動畫和特效,用戶體驗(yàn)至上
    1. 使用簡潔的顯示隱藏,狀態(tài)切換,滑入滑出等效果來展示元素
    2. 使用預(yù)設(shè)值來設(shè)置動畫的速度’fast’,’slow’,或者400(中等速度)

插件

  1. 始終選擇一個有良好支持,完善文檔,全面測試過并且社區(qū)活躍的插件
  2. 注意所用插件與當(dāng)前使用的jQuery版本是否兼容
  3. 一些常用功能應(yīng)該寫成jQuery插件

鏈?zhǔn)秸Z法

jquery支持鏈?zhǔn)秸Z法。很多時候我們可以使用鏈?zhǔn)秸Z法將一系列操作串起來。

除了使用臨時變量緩存jquery對象之外,我們還可以使用鏈?zhǔn)秸{(diào)用。


$("#myDiv").addClass("error").show();

此外,當(dāng)鏈?zhǔn)秸{(diào)用多達(dá)3次以上或代碼因綁定回調(diào)略顯復(fù)雜時,使用換行和適當(dāng)?shù)目s進(jìn)來提高代碼的可讀性。


$("#myLink")
    .addClass("bold")
    .on("click", myClickHandler)
    .on("mouseover", myMouseOverHandler)
    .show();

當(dāng)然,針對特別長的鏈接調(diào)用,最好還是使用臨時變量緩存一下的好。

其他

使用對象直接量傳遞參數(shù)


// BAD, 調(diào)用了3此attr()方法
$myLink.attr("href", "#").attr("title", "my link").attr("rel", "external"); 
// GOOD, 只調(diào)用了一次attr()方法
$myLink.attr({
    href: "#",
    title: "my link",
    rel: "external"
});

不要將css揉進(jìn)jquery中

推薦的方式是,以css class為單位去操作dom元素,而不是直接在dom元素上添加移除css屬性。


$("#mydiv").css({'color':red, 'font-weight':'bold'}); // BAD


error { color: red; font-weight: bold; } /* GOOD */


$("#mydiv").addClass("error"); // GOOD

最好別用已被官方廢棄的方法

這點(diǎn)沒什么好說的。我們需要關(guān)注所使用的具體版本以及官方的changlog即可。這里有一份廢棄方法的列表。

適時的使用原生js

在適當(dāng)或者需要的時候,還是可以使用一些原生js代碼的。這里有一份原文給出的相關(guān)性能測試

譯者注:關(guān)于這一點(diǎn),大家看看就好了,不必較真。在一些js代碼量較大的項(xiàng)目中,是不太會允許這里一坨原生代碼那里一坨原生代碼的。不然維護(hù)的成本會越來越大。性能這一塊,其實(shí)說到底還是得看場景和需求,再一個就是平衡點(diǎn)的把握。

參考列表


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號