jQuery EasyUI的Datagrid組件功能算是很強大了,不過性能確實不怎么樂觀,而對於性能問題,網絡上幾乎也找不到相關的優化資料,所謂的牛人們可能 都望而卻步了。本博客以后會帶着分析Datagrid組件的性能問題,並且給出優化方案,也希望大家能集思廣益,給出一些好的想法。
慢在哪些方面
以目前對Datagrid的了解程度去看待性能問題,主要有以下幾點:
- 加載大數據量時比較慢(不考慮服務端返回數據的時間),這點尤其體現在IE瀏覽器里面;
- 大數據量時,加載后,操作很不流暢,勾選慢,singleSelect為true的話點選也比較慢,IE瀏覽器也是尤其突出;
- 數據量一般,但是字段特別多的話,加載和操作也比較慢,當然了,這種情況比較少見;
- 可編輯表格的性能則是更為糟糕,數據量達到幾十條的時候,操作就會相當不流暢,IE依舊很突出
大數據量的加載
原因分析
不考慮服務端返回數據的時間,在前台獲取到大數據量后,往表格里插入tr的時候,IE執行的效率非常低,2000條數據要45秒左右,其他瀏覽器則很快。
通過單步調試發現,默認視圖在最后將tr寫到table里面用的是jQuery的html()函數,就是這個函數在IE下執行效率非常低。
解決方案一:返璞歸真
jQuery是個很鋒利的工具,可有時候我們也得返璞歸真一下,為什么非要用jQuery的html()函數呢,我們就用javascript dom對象里面的innerHtml屬性不就可以了么,而且換成innerHTML屬性方式的話,效率提高幾十倍。
所以,大數據量加載慢的問題,就這么簡單就解決了,修改默認視圖render方法最后那句:
- //1.3.3版本是這樣的,其它版本也是這句代碼
- $(_1e0).html(_1e4.join(""));
改為:
- $(_1e0)[0].innerHTML = _1e4.join("");
注意:innerHTML雖然符合w3c標准,而且各個瀏覽器也都支持,但是表現出的行為卻又差異,另類的瀏覽器依舊是IE,主要表現在以下幾個方面:
- IE6,IE7,IE8瀏覽器設置innerHTML屬性會忽略html5屬性和標簽,搜索關鍵詞"innerHTML IE html5";
- IE幾乎所有版本設置innerHTML屬性時都會把href,src屬性自動轉化為絕對路徑,搜索關鍵詞"innerHTML IE href";
- IE幾乎所有版本的table相關標簽的innerHTML屬性是只讀的(td除外),搜索關鍵詞"innerHTML IE table;
幸運的是EasyUI的datagrid默認視圖沒有使用html5技術,調用innerHTML的節點也並非table節點(是div),而href,src等轉化為絕對路徑並沒有什么影響。
解決方案二:使用scroll視圖
VirtualScrollView視圖官網已經寫出來了,不過有兩個Bug而已,我對這個視圖的源碼也分析過,請大家參照:
http://www.easyui.info/archives/1404.html
勾選和點選
原因分析
勾選和點選(開啟singleSelect)慢的原因其實是一樣的,都是選擇器執行效率低,這里我拿勾選的情況來分析。
具體的分析過程我就不描述了,知道用chrome,fireBug,IE開發者工具調試的同學,應該都有定位問題的思路:先定位執行效率低的函數,再在函數內定位執行效率低的語句。
checkbox導致操作不流暢的原因,我最后定位到opts.finder.getTr這個方法上,我們來看它的代碼片段:
- if (type == "checked") {
- return (_21d == 1 ? dc.body1 : dc.body2).find(">table>tbody>tr.datagrid-row:has(div.datagrid-cell-check input:checked)");
- }
這段代碼是獲取已經被勾選的rows,大家可以看到,這是純粹的jQuery選擇器查詢,效率就慢在has這個偽選擇器上,它是針對所有后代元素的,查找的效率是比較慢的,又是在這么多數據量的情況下,其效果就可想而知了。
優化方案一:選擇器優化
其實對於checkbox列的DOM結構是固定的,我們完全可以用速度快的選擇器來代替":has",我們先直接用路徑選擇器找到"input:checked",然后使用三次parent()函數返回tr,寫法雖然復雜了,但是效率應該提高一點,所以我們改成這樣:
- if (type == "checked") {
- return (_21d == 1 ? dc.body1 : dc.body2).find(">table>tbody>tr.datagrid-row>td>div.datagrid-cell-check>input:checked").parent().parent().parent();
- }
我用自己的服務大概測試了修改前后的效率(jQuery版本1.8.0,EasyUI版本1.3.3,singleSelect為false,2000條數據勾選一條記錄的測試情況):
瀏覽器 | 執行時間 | 瀏覽器 | 執行時間 | |
---|---|---|---|---|
原版 | IE9 | 600ms | chrome | 60ms |
選擇器優化 | IE9 | 560ms | chrome | 60ms |
從上面的結果可以看出,在這種測試條件下,我們提高的效率並不大,IE9下提高的效率盡管有所提高,但是還是很不理想,而chrome下性能基本一 樣。測試過程中發現,如果使用jQuery2.0的話,IE9下的執行時間將達到45000ms,幾乎讓人奔潰,看來盡管IE9勉強支持 jQuery2.x,但是效率很挫。
優化方案二:實時記錄優化法
既然慢在DOM結構巨大時,jQuery選擇器的搜索效率不是很好(特別是在IE下)。如果我們每次操作都記錄下勾選的tr,那么就完全可以繞開選擇器。
具體該怎么做呢,我們給$.data(target,'datagrid')變量增加兩個屬性:"checkedTrsBody1" 和"checkedTrsBody2"分別存儲frozen部分和normal部分被勾選tr的引用,然后在各個設計到勾選的操作中維護這兩個屬性。最 后,獲取被勾選tr的時候就可以直接從這兩個屬性中取了,其耗時是可以忽略的。
那么究竟哪些操作會影響到被勾選的tr呢,我們羅列一下,也就以下幾 種:"checkRow","uncheckRow","uncheckAll","checkAll","deleteRow","loadData","load","reload". 我們只要在這些接口中維護起"checkedTrsBody1"和"checkedTrsBody2"屬性姐可以了。
至於具體的代碼怎么改,我就不貼了,最好就直接改動源碼了,思路很清晰,請各位自己去實現,是在理不出頭緒的,請參照我的實現:
http://www.easyui.info/version/jquery-easyui-1.3.3/plugins/jquery.datagrid.js
數據報表統計
以下是一些測試報表,測試環境請看報表標題(Y軸為耗時,單位毫秒;X軸為數據量):
可以看出來,無論是在IE9下,勾選效率都提高了很多倍(chrome下效率也有顯著提高)。對於開啟singleSelect的優化思路是一樣的,所以不寫重復文字了。
對於原版的datagrig,我本地的測試環境數據在4000條以上時,IE9基本就卡死了(可能機器性能不太好),無統計價值了,即便是4000條數據,也要將近2分鍾才渲染完,顯然沒人能夠忍受。
從報表很明顯可以看出優化過的表格,即便是10000條數據,3秒也就渲染完成了。
優化演示
未優化版本:http://www.easyui.info/version/jquery-easyui-1.3.3/demo/datagrid/bigdata_checkbox.html
優化版本:http://www.easyui.info/version/jquery-easyui-1.3.3/demo/datagrid/bigdata_checkbox_optimized.html