jQuery高級應用:優化Web應用程序的最后絕招

作者: Michael Abernethy  發布時間: 2010-10-28 15:42  閱讀: 2481 次  推薦: 0   原文鏈接   [收藏]  
 
摘要:在本文中,我將討論代碼的性能改進,同時也談及一些關于jQuery庫的容易被忽視的問題。有些東西對于復雜的應用程序而言至關重要,即對所有應用程序都非常關鍵的插件,以及使應用程序的編寫更加容易的良好設計技巧。
[1] jQuery高級應用:優化Web應用程序的最后絕招
[2] jQuery高級應用:優化Web應用程序的最后絕招

  jQuery讓編寫基于JavaScript的良好Web應用程序變得簡單明了,但是要想將好的Web應用程序變得更出色還需要額外幾個步驟。本文詳細闡述幾個讓Web應用程序變得更強大的步驟,這些步驟是優化您的Web應用程序的最后絕招。

  第一個示例應用程序

第一個示例應用程序

  本文中的大部分技巧都可以從附帶的樣例應用程序中找到,這是一個直觀的電子郵件Web應用程序。不過,您可以看到它是如何從第一篇文章中發展而來的,它的性能是如何改進的,以及這些最后步驟如何將它轉變成強大的Web應用程序的。

  Bind/Unbind

  在Events模塊中有兩個函數,它們是bind()和unbind(),用于完成所有其他事件方法的任務。如果您能夠向頁面元素添加一個click()方法,那么哪還有必要調用bind("click")呢?這僅是浪費時間而已。但是,這些函數在特定情況下是非常方便的,如果正確地使用它們,可以顯著提高應用程序的性能。

Bind/Unbind

  這些函數不僅能夠向特定頁面元素添加事件(就像該模塊中的許多其他事件方法一樣),而且還可以從頁面元素中刪除這些事件。為什么要這樣做?下面我們看看這個Web應用程序,以及如何在特定情況下使用這些函數。

清單1顯示了以上設置的代碼,這是未改進之前的原始代碼:

 
1. $(document).ready(function(){
2. //cachethisquerysinceit'sasearchbyCLASS
3. selectable=$(":checked.selectable");
4. //whentheselect/deselectallisclicked,dothisfunction
5. $("#selectall").click(selectAll);
6. //wheneveranyindividualcheckboxischecked,changethetext
7. //describinghowmanyarechecked
8. selectable.click(changeNumFilters);
9. //calculatehowmanyareinitiallychecked
10. changeNumFilters();
11. });
12.
13. varselectable;
14. functionchangeNumFilters()
15. {
16. //thisneedstobecheckedoneverycall
17. //sincethelengthcanchangewitheveryclick
18. varsize=$(":checked.selectable").length;
19. if(size>0)
20. $("#selectedCount").html(size);
21. else
22. $("#selectedCount").html("0");
23. }
24.
25. //handlestheselect/deselectofallcheckboxes
26. functionselectAll()
27. {
28. varchecked=$("#selectall").attr("checked");
29. selectable.each(function(){
30. varsubChecked=$(this).attr("checked");
31. if(subChecked!=checked)
32. {
33. $(this).click();
34. }
35. });
36. changeNumFilters();
37. }

  該代碼看起來比較簡單,因為我在好幾篇文章中都用到這個小部件。您在第一篇文章中見到了“select/deselectall”小部件,我給出了它的基礎形式。在關于性能的文章中,您看到如何通過緩存選擇查詢和通過CLASS減少使用查詢來改進它的性能。但是還有一個問題需要解決。當在包含100行的表中勾選“select/deselectall”復選框之后,您將得到糟糕的性能。事實上,在我的瀏覽器中,如果使用了這些代碼,那么完成選擇的平均時間為3.4秒。響應性太差了!即使進行了各項優化,仍然有些不可接受的地方。

  讓我們深入一步考察這個算法,看看是否有地方出了問題。您將遍歷頁面上的每個復選框,看看它們當前的“checked”狀態是否與“select/deselectall”復選框一致。如果不一致,就對其調用“click”,以和“select/deselectall”復選框的狀態匹配。等一等,您還需要向這些復選框添加一個函數,從而在每次單擊時都調用changeNumFilters()函數。

  通過仔細檢查,您發現設置了一個可能調用changeNumFilters()101次的算法。怪不得性能如此差。很明顯,您不需要在每次單擊時都更新選中的消息的計數,而是在該過程完成之后進行更新即可。在單擊復選框的同時如何才能避免調用該方法?

  現在,unbind()方法開始發揮它的作用。通過在單擊復選框之前調用unbind(),將停止調用click(),同時避免了click()進一步調用changeNumFilter()方法。這很棒!現在就不會調用changeNumFilters()101次了。但是,這僅是一次有效的,在調用click方法之后,需要使用bind方法將click方法添加回到每個復選框。清單2顯示了更新之后的小部件。

 
1. //handlestheselection/unselectionofallcheckboxes
2. functionselectAll()
3. {
4. varchecked=$("#selectall").attr("checked");
5. selectable.unbind("click",changeNumFilters);
6. selectable.each(function(){
7. varsubChecked=$(this).attr("checked");
8. if(subChecked!=checked)
9. {
10. $(this).click();
11. }
12. });
13. selectable.bind("click",changeNumFilters);
14. changeNumFilters();
15. }

  通過這些優化之后,復選框的運行速度提高到約900毫秒,從而大大改進了性能。這些改進源于返回去檢查您的算法正在做什么,以及貫穿代碼的操作。您可以僅調用函數1次,而不是100次。通過在本系列的其他文章中不斷改進該函數,您最后會讓它變得更快、更高效。但不一定非得這么做,我還發現一個最快的算法,以前從來沒有透露過。此外,如果我過早地向您展示這個最快的算法,我就不能將其作為本文的題材了。希望它能使您看到在代碼中使用bind/unbind特性帶來的好處(如果沒有更好的方法的話)。

  記住:在不希望觸發默認事件時才使用bind/unbind,或作為向頁面元素添加或刪除事件的臨時方法

  清單3顯示了編寫該算法的最快方法(如果您的代碼中有這個小部件)。它運行該函數僅需40毫秒,遠遠勝過之前的其他方法。

 
functionselectAll()
{
varchecked
=$("#selectall").attr("checked");
selectable.each(function(){
$(
this).attr("checked",checked);
});
changeNumFilters();
}

  Live/Die

  jQuery 1.3版本的另外兩個強大的新特性是live()和die()函數。通過一個示例可以看到它對構建設計良好的Web應用程序的作用。想像一下對表中的每個單元格都添加一個雙擊。作為jQuery老手,您應該知道要在document.ready()函數中設置雙擊,如清單4所示。

 
1. $("tr.messageRow").dblclick(function(){
2. if($(this).hasClass("mail_unread"))
3. {
4. $(this).removeClass("mail_unread");
5. }
6. });

  這個設計存在一個問題。它向包含一個messageRow類的表的每行添加一個雙擊事件。但是,如果向該表添加新的行,會發生什么事情呢?例如,當您使用Ajax在未重新加載頁面的情況下將額外的消息加載到頁面時,可能會顯示這些行。這導致一個問題,因為所編寫的代碼不能工作。您創建的事件被綁定到所有在加載頁面時顯示的tr.messageRow元素中。

  它沒有綁定到您在頁面的生命周期中創建的任何新的tr.messageRow中。編寫類似代碼的程序員最終會很失望的,因為它無法工作。在jQuery文檔發現該問題之前,jQuery新手可能要花好幾個小時才能弄明白為什么他們的代碼不能工作(這也是我去年的經歷)。

  在jQuery 1.3之前,有3種可以解決該問題的方法,但都不是很好(對于使用jQuery 1.2.x的程序員而言,它們仍然有效)。第一種方法是重新初始化技術,它在每次添加新的元素之后重新將事件添加到選中的元素中。第二種方法利用了bind/unbind,如前面小節所示。清單5顯示了這兩種方法。

 
1. //firsttechniquetodealwith"hot"pageelements,addedduringthepage's
2. //lifetime
3. $("#mailtabletr#"+message.id).addClass("messageRow")
4. .dblclick(function(){
5. if($(this).hasClass("mail_unread"))
6. {
7. $(this).removeClass("mail_unread");
8. }
9.
10. //secondtechniquetodealwith"hot"pageelements
11. $("#mailtabletr#"+message.id).addClass("messageRow")
12. .bind("dblclick",(function(){
13. if($(this).hasClass("mail_unread"))
14. {
15. $(this).removeClass("mail_unread");
16. }

  這兩種方法都不是很好。您可能正在重復編寫代碼,或者被迫查找可能添加新頁面元素的點,然后在這些點上處理“熱元素”問題。這不是良好的編程方式。但是,jQuery可能大大簡化這一切,它能夠幫助我們完成很多事情。

  幸運的是,有一個插件好像能夠解決該問題。這個插件稱為LiveQuery插件,它允許您將特定頁面元素綁定到事件,但僅能以“活動”的方式進行,因此所有頁面元素(包括創建頁面時自帶的元素和在頁面的生命周期中創建的元素)都可能觸發事件。對UI開發人員而言,這是一個非常智能、重要的插件,因為使得處理動態頁面就像處理靜態頁面一樣簡單。對于Web應用程序開發人員而言,它就是真正不可或缺的插件。

  jQuery核心團隊意識到該插件的重要性,從而將其包含到1.3發布版中。這個“活動”特性現在是核心jQuery的一部分,因此任何開發人員都可以利用它。這個特性完整地包含在1.3核心代碼中,除了一小部分事件之外。我敢打賭,這些未被包含的事件將出現在jQuery的下一個發布版之中。我們看看如何利用它改變代碼。

 
1. $("tr.messageRow").live("dblclick",function(){
2. if($(this).hasClass("mail_unread"))
3. {
4. $(this).removeClass("mail_unread");
5. }

  通過對代碼進行一處小更改,該頁面上的所有tr.messageRow元素被雙擊時都將觸發這段代碼。僅使用dblclick()函數是看不到這種行為的,如上所述。為此,我極力推薦您在大部分事件方法中使用live()方法。事實上,我認為它對于任何動態地創建頁面元素的頁面都是必不可少的,不管是通過Ajax還是用戶交互進行創建,都需要使用live()函數而不是事件方法。它很好的實現了易編寫和bug之間的折衷。

  記住:當將事件添加到動態頁面元素時要使用live()方法。這讓事件和頁面元素一樣具有動態性。

  Ajax Queue/Sync

  在服務器中使用Ajax調用成為Web2.0公司度量自身的度量指標。我們已經多次討論過,在jQuery中使用Ajax就像調用普通的方法一樣簡單。這意味著您可以輕松地調用任何服務器端Ajax函數,就像調用客戶端的JavaScript函數一樣。但是美中存在一些不足之處,當對服務器進行過多的Ajax調用時,就會出現一些負面效應。如果Web應用程序使用的Ajax調用過多,就會導致問題。

  第一個問題是一些瀏覽器限制打開的服務器連接的數量。在Internet Explorer中,當前版本僅支持打開2個服務器連接。Firefox支持打開8個連接,但仍然是一個限制。如果Web應用程序不對Ajax調用進行控制,它就很可能打開2個以上的連接,尤其是服務器端調用屬于時間密集型調用時。這個問題可能源于Web應用程序的不良設計,或用戶不對請求加以限制。不管是那種情況都是不可取的,因為您不希望由瀏覽器決定使用哪些連接。

  另外,因為調用是異步的,不能保證從服務器返回的響應的順序與發送時一樣。例如,如果您幾乎同時發出2個Ajax調用,您就不能保證服務器的響應是以相同的順序返回。因此,如果第二個調用依賴于第一個調用的結果,那么就會出現問題。想象這樣一種場景,其中第一個調用獲取數據,第二個調用在客戶端操作該數據。如果第二個調用的響應返回得比第一個Ajax調用快,那么您的代碼就會導致錯誤。您完全不能保證響應速度。當調用更多時,就更容易導致問題。

  jQuery的創建者意識到這個潛在的問題,但同時也認識到它僅會給1%的Web應用程序帶來問題。但1%開發Web應用程序的開發人員需要一個解決辦法。因此創建了一個插件,通過創建一個Ajax Queue和一個Ajax Sync來篩查該問題。Queue和Sync的功能是很相似的:Queue一次發出一個Ajax調用,并且等待其響應返回之后才發出另一個調用。Sync幾乎同時發出多個調用,但調用的響應是按先后順序返回的。

  通過在客戶端控制Ajax調用解決了超載問題,同時也控制和規范了將響應發送回客戶端的方式。現在,您可以確保知道響應返回到客戶端的順序,從而可以根據事件的順序編寫代碼。我們看看這個插件是如何工作的,以及如何在您的代碼中使用它(見清單7)。記住,這僅是為1%的Web應用程序設計的,它們擁有多個Ajax調用,并且后一個調用嚴重依賴于前一個調用的結果。這個示例不是調用相互依賴的例子,但它能夠向您展示如何使用該插件(要為這個插件的應用創建一個完美的真實例子,并讓其易于理解是很困難的)。

 
1. varnewRow="<trid='?'>"+
2. "<td><inputtypeinputtype=checkboxvalue='?'></td>"+
3. "<td>?</td>"+
4. "<td>?</td>"+
5. "<td>?</td>"+
6. "<td>?</td></tr>";
 
1. $("#mailtable").everyTime(30000,"checkForMail",function(){
2. //byusingtheAjaxQueuehere,wecanbesurethatwewillcheckformail
3. //every30seconds,butONLYifthepreviousmailcheckhasalreadyreturned.
4. //Thisactuallywouldbebeneficialinamailapplication,ifonecheckfor
5. //newmailtakeslongerthan30secondstorespond,wewouldn'twantthe
6. //nextAjaxcalltokickoff,becauseitmightduplicatemessages(depending
7. //ontheserversidecode).
8. //So,byusingtheAjaxQueueplug-in,wecanensurethatourWebclient
9. //isonlycheckingfornewmailonce,andwillneveroverlapitself.
10.
11. $.ajaxQueue({
12. url:"check_for_mail.jsp",
13. success:function(data)
14. {
15. varmessage=eval('('+data+')');
16. if(message.id!=0)
17. {
18. varrow=newRow.replace("?",message.id);
19. rowrow=row.replace("?",message.id);
20. rowrow=row.replace("?",message.to);
21. rowrow=row.replace("?",message.from);
22. rowrow=row.replace("?",message.subject);
23. rowrow=row.replace("?",message.sentTime);
24. $("#mailtabletbody").prepend(row);
25. $("#mailtable#"+message.id).addClass("mail_unread").addClass("messageRow");
26. $("#mailtable#"+message.id+"td").addClass("mail");
27. $("#mailtable:checkbox").addClass("selectable");
28. }
29. }
30. });

  記住:如果您的應用程序有多個相互依賴的Ajax調用,那么要考慮使用Ajax Queue或Ajax Sync。

[第1頁][第2頁]
0
0
 
標簽:jQuery Web
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()