【翻譯】Ext JS 4.1的效能

來源:互聯網
上載者:User

原文:http://www.sencha.com/blog/ext-js-4-1-performance/
 

在本文,將講述幾個影響Ext JS應用效能的因素。

  • 網路延時會嚴重影響初始化啟動時間,尤其是Store的載入時間。
  • CSS處理。
  • Javascript的執行。
  • DOM操作。
網路延時

為了最大限度的減少應用啟動時間,必須牢記的是,任何域對瀏覽器的網路連接並發數量是有限制。


這意味著,如果從一個域請求許多檔案,一旦達到上限,隨後的下載將要排隊,只有當一個串連槽釋放時,他們才會被處理。新的瀏覽器都有較高範圍,但對舊的、慢的瀏覽器進行最佳化就很重要。


解決辦法是使用Sencha SDK工具將應用所需的指令檔產生一個單一的串聯的指令檔。


具體資訊可查看Ext JS 4.0入門。

 

SDK的“create”命令會分析載入到頁面的應用,載入類定義中requires和user屬性引用的所有檔案,然後以正確的順序建立一個包含所有所需的類定義的指令檔。


可在Ext JS 4.1的文檔中查閱Sencha類系統的詳細資料。

 

另外一種減少網路延時的方法是在Web伺服器對Ext JS的頁面及其相關的指令碼和樣式檔案啟用GZIP壓縮。

CSS處理

CSS選擇是跟隨DOM的父節點指標從右至左匹配的。

這意味著以下的樣式處理過程是,現在文檔中找到匹配的span,然後沿著父節點這條軸去尋找符合兩個指定樣式類名的祖先節點。

 
  1. .HeaderContainer .nav span 

因而,在元素上使用單一的,確定類名的樣式會更高效。

 

Javascript執行
 

最佳化Javascript代碼必須牢記以下幾點:

  • 避免使用舊的或差的Javascript引擎的寫法。
  • 最佳化經常重複出現的代碼。
  • 最佳化在渲染或布局時執行的代碼
  • 最好是盡量不要在初始化渲染或布局時執行任何額外的代碼。
     
  • 將不變的運算式移到迴圈外面。
  • 使用for迴圈代替 Ext.Array.each。
  • 如果函數中條件內代碼經常被調用,必要時,將條件移到函數外可參考Ext JS程式碼程式庫對fireEvent的調用)。
  • 在差的Javascript引擎安裝和拆卸調用架構讓函數調用所需的裝置)會很慢。
代碼最佳化樣本

試想一下,一個統計應用為Grid的資料列提供了一些操作,在Grid的欄位標題菜單中增加功能表項目,以便對任何調用列進行所需的操作。


處理常式必須獲得相關的上下文資訊及將要進行操作的當前活動的欄位標題資訊:
 

650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131228/130I55546-0.png" />

在GitHub的樣本示範了兩種執行這個操作的方式,它可以運行在任何版本的SDK樣本目錄下。

以下是第一種用來合計活動列的寫法:

 
  1. function badTotalFn(menuItem) {  
  2.    var r = store.getRange(),  
  3.        total = 0;  
  4.    
  5.    Ext.Array.each(r, function(rec) {  
  6.        total += rec.get(menuItem.up('dataIndex').dataIndex);  
  7.    });  

這裡有幾個錯誤做法。首先,使用Ext.each為數組中的每個記錄調用傳遞函數。正如看到的,函數設定會影響效能。其次,menuItem.up('dataIndex')運算式的結果是不變的,它只需要執行一次,可以放到迴圈之外。
 

 

因此,可將代碼最佳化成以下代碼:

 
  1. function goodTotalFn(menuItem) {  
  2.     var r = store.getRange(),  
  3.         field = menuItem.up('dataIndex').dataIndex;  
  4.         total = 0;  
  5.    
  6.     for (var j = 0, l = r.length; j < l; j++) {  
  7.         total += r[j].get(field);  
  8.     }  
  9.  } 

這可能似乎微不足道的差異,但效能差異顯著。

 

在下面表格,計算功能迭代了10000次後,提供了一個可衡量的時間:

Browser Bad Good
Chrome 1700ms 10ms
IE9 18000ms 500ms
IE6 Gave up 532ms


正如所看到的,即使沒有迭代,使用第一個方式,IE9都要花費1.8秒去處理操作。

使用頁面分析器去衡量效能

頁面分析器是SDK的一個樣本,在example/page-analyzer目錄可找到它。它會在捕捉架構中運行同一個域的頁面,然後捕捉Ext JS樣本,以便分析布局效能和任何對象的任何方法的效能。

如果是使用Chrome,需要在它的命令列使用“-enable-benchmarking”命令開啟微秒計時精度。

要分析以上樣本的關鍵位置的效能,切換到“效能”標籤,然後在左下角的標籤面板選擇“Accumulators”標籤,並粘貼以下代碼到textarea:

 
  1. {  
  2.     "Component.up": {  
  3.         "Ext.Component": "up" 
  4.     },  
  5.     "CQ.is": {  
  6.         "Ext.ComponentQuery": "!is" 
  7.     }  

確保迭代兩個合計計算函數10000次,才能獲得準確的效能狀態。

 

然後在頁面分析器中載入Grid效能測試樣本,使用“Get total in bad way”開始合計,並單擊頁面分析器的右上方“UPdate Stats”。

650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131228/130I55311-1.png" />

然後單擊“Reset”清空accumulators,增加“Build”計時器,使用“Get total in good way”進行合計並單擊頁面分析器的“Update Stats”按鈕。

在“效能”標籤的“Grid”子標籤內,會看到所示的兩個運行結果:

 

650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131228/130I51931-2.png" />

可以看到,不變表單式在迴圈外調用ComponentQuery方法的次數要少得多。

合并多個布局操作

Ext JS 4會根據內容變化或大小變化自動進行布局。這意味著,當一個按鈕的文本變化,會導致它所在的工具列重新布局因為按鈕的高度可能會改變),而工具列所在的面板也要重新布局因為工具列的高度可能改變)。

基於這個原因,在內容或大小發生改變時,將多個布局操作合并在一起顯得非常重要,可以使用以下代碼實現:

 
  1. {  
  2.     Ext.suspendLayouts();  
  3.     // batch of updates  
  4.     Ext.resumeLayouts(true);  

參數true的傳遞意味著重新啟用布局,它運行到這點時,會清理任何排隊的布局請求。
 

 

減少DOM負擔

通過減少嵌套的容器或組件以實現儘可能少的層數相當重要,這可以避免重複布局運行和DOM迴流,因為這些的開銷是很昂貴的。

另外一個要遵循的原則是使用最簡單的容器或布局做必要的工作。

常見的例子是在將一個Grid放到TabPanel的時候再嵌套一個組件。以下就是習以為常的寫法:

 
  1. {  
  2.     xtype: "tabpanel",  
  3.     items: [{  
  4.         title: "Results",  
  5.         items: {  
  6.             xtype: "grid" 
  7.             ...  
  8.         }  
  9.     }]  

不知道為什麼要添加一個普通面板作為標籤面板的子條目,然後再面板內放置Grid,而這樣的嵌套層毫無用處。

 

而事實上,這破壞了標籤面板的運作,因為沒有為封裝的面板配置布局,從而不能處理它的子組件Grid的大小變化,這意味著它不能自適應標籤面板的大小,並根據標籤面板的高度進行滾動。

正確的寫法是:

 
  1. {  
  2.     xtype:”tabpanel“,  
  3.     items: [{  
  4.         title: ”Results“,  
  5.         xtype: ”grid“,  
  6.         ...  
  7.     }]  
為什麼這個原則很重要?

保持組件樹或DOM樹)儘可能的輕量化是相當重要的,其主要原因是在Ext JS 4中,許多組件是帶有子組件的容器並有執行自己的布局管理器,例如,面板標題現在是一個容器類,在它裡面可以配置除標題文本和工具按鈕之外的其它的組件。

雖然將標題作為層次化的容器帶來了額外的開銷,但是這讓UI設計更靈活了。
 

此外,在Ext JS 4中,組件會使用組件布局管理器去管理內部DOM結構的大小和位置,而不象Ext JS 3.x那樣,使用onResize方法去處理。

可視化的組件樹

在遵循以上原則去設計UI的時候,可以把UI想象為樹結構,例如,一個Viewport可以想象為:

 

650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131228/130I55S0-3.png" />

為了渲染yield組件樹,它會運作2次。

第一遍,會調用每個組件的beforeRender,然後調用getRenderTree產生可產生HTML標記的DomHelper的設定物件,並將它添加到渲染緩衝中。

第一遍之後,一次性將代表整樹的HTML代碼插入到文檔中,這樣,就減少了建立應用結構時的DOM處理。

然後樹再走一遍,調用每個組件onRender方法將元件連線到他們相關的DOM節點。然後調用afterRender完成渲染處理。

在此之後,完整的初始布局就執行完了。

而這就是為什麼建立輕量級UI非常重要的原因。

研究一下下面面板的構成:

 
  1. Ext.create('Ext.panel.Panel', {  
  2.     width: 400, height: 200,  
  3.     icon: '../shared/icons/fam/book.png',  
  4.     title: 'Test',  
  5.     tools: [{  
  6.         type: 'gear' 
  7.     }, {  
  8.         type: 'pin' 
  9.     }],  
  10.     renderTo: document.body  
  11. }); 

它會導致相當複雜的UI結構:
 

 

 

650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131228/130I52647-4.png" />

儘可能避免收縮封裝shrinkwrapping,根據內容自動調整大小)

儘管Ext JS 4提供了可自動根據內容調整大小的容器在Ext JS中簡稱為“shrinkwrapping”),但這會加重部分布局的負擔,其次是計算結果會造成瀏覽器的迴流,隨後還要使用計算的高度和寬度重新布局。

避免DOM重新整理大小,可提高效能。

儘可能避免限制大小minHeight, maxHeight, minWidth, maxWidth)

如果限制被命中,那麼整個布局就要進行重新計算,例如,一個使用flex定義的盒子布局的子組件在收到其計算寬度小於定義的minWidth時,它會被固定為最小寬度,然後整個盒子布局將不得不重新進行計算。

類似的情形是,一個盒子布局使用stretchMax配置項時,所有子組件將切換為固定的垂直尺寸例如,Hbox布局高度),而布局則會重新計算。

避免在渲染後處理組件的DOM

為了避免DOM迴流和重畫,盡量避免在組件渲染後處理其DOM結構。替代辦法是在產生HTML代碼之前,使用提供的鉤子去修改組件的配置。

如果實在是要修改ODM結構,重寫getRenderTree方法是最後的方式。

Grid的效能

表格的大小會影響效能,尤其是列的數量,因而要保持儘可能少的列數。

如果資料集非常大,而且不想在UI中使用分頁工具條,那麼就使用俗稱為“無限Grid”的緩衝渲染方式。

要實現這個,只需要在Store中添加以下配置項:

 
  1. buffered: true,  
  2. pageSize: 50, // Whatever works best given your network/DB latency  
  3. autoLoad: true 

然後像往常一樣載入和維護它。

 

它是如何工作的

Grid會計算渲染表格的大小並根據PagingScroller對象的配置項來監控滾動位置。以下是滾動時需要配置的配置項:

  • trailingBufferZone :保持在可視地區上的已渲染記錄數。
  • leadingBufferZone:保持在可視地區下已渲染的記錄數。
  • numFromEdge:在表格重新整理之前,表格滾動時與可視地區之間的邊界值。

渲染的表格需要包含足夠的行數來填充視圖的高度,還要加上緩衝期的大小,以及前置緩衝區的大小,再加上numFromEdge * 2)以便建立滾動溢出。

表格的滾動結果,會被監控,並在表格末尾與視圖之間的行數小於numFromEdge時,使用資料集中的下一塊資料重新渲染表格,然後定位,以便讓行的可視位置不變。

在最好的情況,重新渲染所需的行已經在頁面緩衝中,而操作是瞬時的和察覺不到的。

要配置這些值,可在Grid的verticalScroller配置項中配置:

 
  1. {  
  2.     xtype: 'gridpanel',  
  3.     verticalScroller: {  
  4.         numFromEdge: 5,  
  5.         trailingBufferZone: 10,  
  6.         leadingBufferZone: 20  
  7.     }  

這意味著將有40行的溢出資料提供給Grid可視地區實現平滑滾動,而重新渲染將會在表格邊界與可視地區之間少於5行時發生。

 

保持管道完整

保持頁面緩衝為將來的滾動準備資料是Store的工作。Store也有trailingBufferZone和leadingBufferZone。

每當表格請求重新渲染的行時,在返回請求行之後,Store會確保緩衝中的資料涵蓋兩個地區所需的資料,如果資料不在緩衝,則會向伺服器請求資料。

這兩個地區都有相當大的預設值,開發人員可以調小或調大他們保持在管道中的頁數。

緩衝失敗

當“瞬移”到資料集不在緩衝部分時,會顯示載入遮蔽和延時渲染,因為需要從伺服器請求資料,不過,這種情況已經最佳化過了。

包含顯示地區所需資料的頁面範圍會優先發生請求,當資料一到達就立刻重新渲染。 trailingBufferZone 和leadingBufferZone所需的資料將會在UI所需資料載入後立刻發送請求。


 

修剪緩衝

預設情況下,緩衝會計算最大尺寸,除此之外,它還會丟棄最近使用的頁。頁面數量的大小為捲軸的leadingBufferZone加上可視地區大小,再加上trailingBufferZone和Store的purgePageCount配置項。增加purgePageCount意味著一旦一個頁面被訪問,就可以很快的返回它,而不是向伺服器發送請求。

如果purgePageCount的值為0,意味著緩衝可以不斷增長,而不用修剪,最終可能增長到包含整個資料集。這在資料集不是大得離譜時是一個非常有用的選項。記住,人類無法理解太多的資料,因而在Grid顯示多於千行的資料實際上沒有多大用處,這可能意味著,他們使用了錯誤過濾條件並需要重新查詢。

將整個資料集放在用戶端

如果資料集不是天文資料集,將整個資料集緩衝在頁面是可行的。

可以通過SDK樣本目錄下的examples/grid/infinite-scroll-grid-tuner.html)的“Infinite Grid Tuner”樣本來測試下其可行性。

如果設定Store的leadingBufferZone為50000,並設定purgePageCount為0,這將產生預期的效果。

leadingBufferZone會讓Store去保持管道完整,50000意味折非常完整。

purgePageCount為0意味著頁數的增長沒有限制。

因此,當單擊“Reload”,會看到可視地區需要的資料頁會最先被請求,然後渲染。

然後,會看到Store會努力去填滿巨大的leadingBufferZone。很快,整個資料集就被緩衝了,然後,在捲動區域任何地方的資料訪問都是即時的。


 

作者:Nige "Animal" White
Nigel brings more than 20 years experience to his role as a software architect at Sencha. He has been working with rich Internet applications, and dynamic browser updating techniques since before the term "Ajax" was coined. Since the germination of Ext JS he has contributed code, documentation, and design input.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.