KendoGrid+Angular顯示大量資料的效能問題
8 |
接獲使用者反應,某個使用KendoGrid顯示大量資料的網頁,用IE檢視的話速度慢到嚇人。聞此言,馬上打開IE11測試,果真嚇得我差點閃了兩滴… 也太慢了吧!
先交代問題情境,專案使用Angular搭配Kendo UI開發,順理成章使用Kendo UI提供的Angular Directive,寫成<div kendo-grid …>,資料筆數偏多,大約一千多到兩千筆,基於KendoGri的強大彈性及高複雜度,處理起來多耗點時間倒也合理,但這個案例在IE的表現與期望出入頗大,值得調查。
修改KendoGrid的官方範例,我設計了一個實驗:
<div id="example" ng-app="KendoDemos">
<div ng-controller="MyCtrl">
<button ng-click="loadData()">Load Data</button>
<span ng-bind="duration"></span>
<div kendo-grid="grid" options="mainGridOptions">
</div>
</div>
</div>
<script>
angular.module("KendoDemos", [ "kendo.directives" ])
.controller("MyCtrl", function($scope){
var data = [];
for (var i = 0; i < 2000; i++) {
data.push({
FirstName: "FN" + i,
LastName: "LN" + i,
Country: "CN" + i,
City: "CT" + i,
Title: "T" + i
});
}
$scope.loadData = function () {
st = new Date();
$scope.grid.dataSource.data(data);
};
var st;
$scope.mainGridOptions = {
dataSource: [],
height: 600,
sortable: true,
pageable: false,
columns: [
{ field: "FirstName", title: "First Name", width: "120px" },
{ field: "LastName", title: "Last Name", width: "120px" },
{ field: "Country", width: "120px" },
{ field: "City", width: "120px" },
{ field: "Title" }
],
dataBound: function () {
if (st) {
setTimeout(function () {
$scope.duration = new Date() - st + "ms";
$scope.$digest();
}, 1);
}
}
};
})
</script>
網頁使用Directive建立KendoGrid,按鈕後將2000筆資料放入KendoGrid中,重點按鈕到資料顯示完成所需的時間(dataBound事件將在整個Grid的DOM生成後觸發),實測IE11耗時3秒。透過F12開發者工具的分析工具,抓出速度慢的源頭在Angular的compile、publicLinkFn等方法,共執行1979次,耗費2秒以上。
此一發現讓人合理推測如果不用Angular Directive,改以純jQuery方式建立KendoGrid,效率應會提升。所以我又寫了對照組:
<div id="example">
<div>
<button>Load Data</button>
<span id="duration"></span>
<div id="grid">
</div>
</div>
</div>
<script>
var data = [];
for (var i = 0; i < 2000; i++) {
data.push({
FirstName: "FN" + i,
LastName: "LN" + i,
Country: "CN" + i,
City: "CT" + i,
Title: "T" + i
});
}
var st, ed;
$("#grid").kendoGrid({
dataSource: [],
height: 600,
sortable: true,
pageable: false,
columns: [
{ field: "FirstName", title: "First Name", width: "120px" },
{ field: "LastName", title: "Last Name", width: "120px" },
{ field: "Country", width: "120px" },
{ field: "City", width: "120px" },
{ field: "Title" }
],
dataBound: function () {
if (st) $("#duration").text(new Date() - st + "ms");
}
});
$("button").click(function () {
st = new Date();
$("#grid").data("kendoGrid").dataSource.data(data);
});
</script>
幾乎相同的程式邏輯,差別在改用jQuery建立KendoGrid及處理按鈕事件,實測速度可縮短至1.2秒,證明速度慢確實與啟用Angular有關。使用分析工具觀察,同樣的欄位設定,不使用Angular時,KendoGrid在_renderContent()經由指定innerHTML產生畫面元素,省去Angular模式的comple、建立scope、產生dataItem物件環節,邏輯相對單純,應是耗費時間變少的主因。
不過,這種模式不能在欄位Template使用<span ng-bind="dataItem.propName">等Angular寫法,無法叫用現成的Directive、Filter等,不利邏輯集中與程式碼共用。有利有弊,實務應用時就必須做出取捨,而二者的效能差距數字是判斷的關鍵。
寫了兩個測試網頁放在JSBin:KendoGrid + Angular版 vs KendoGrid + 純jQuery,用IE11、Edge、Chrome及Firefox做了不專業的測試。操作方法是重新載入網頁,按下Load Data鈕,反覆數次取最低值,測試結果為:
- IE11:2.3秒 vs 0.8秒
- Edge:2.2秒 vs 0.8秒
- Chrome:1.1秒 vs 0.3秒
- Firefox:1.1秒 vs 0.3秒
由於沒有精準地控制環境、變因,以上數據並不具權威性,但足以確定啟用Angular時速度慢了約三倍,在資料量小的情境差別有限,若遇到上千筆資料時,就有可能讓使用者皺眉。而這個問題在IE上又格外嚴重,在面對類似情境時要特別留意,甚至放棄Angular Template回歸純jQuery模式改善效能。
留下兩點值得探討:
- 原本預期Edge的效能應逼近Chrom,但實測卻跟IE11近乎相同,有一種可能是KendoGrid的程式有針對不同瀏覽器優化,Edge未受惠,或是我的測試環境有問題,歡迎使用Windows 10的朋友幫忙複測看看。
- 直覺KendoGrid使用Angular架構產生資料列所遇到的效能議題,在巨量ng-repeat或網頁內含大量採用Template生成DOM的Directive時也將面臨相同挑戰,這點留待日後驗證。
Comments
# by player
有考慮改用 DataTables 嗎? 如果做成分頁顯示的話, 應該會快不少 https://www.datatables.net/
# by Fish
Edge用 jQuery要8.8秒!?筆誤?
# by pbnttttt
kendo grid 有提出解決方案「Virtualization 」,不知對於黑大的case是否適用? http://demos.telerik.com/kendo-ui/grid/virtualization-local-data
# by Jeffrey
to Player, KendoGrid有些難以取代的特性,像是欄位凍結、跟Angular的整合性等。不過DataTable看起來是好東西,謝謝分享。在客戶端分頁應該也是個好主意。 to Fish,是筆誤沒錯 orz,謝謝指正。 to pbnttttt,想讓使用者可以快速排序、用關鍵字篩選資料,所以沒想到Virtualization,但應該也是另一種解套的做法,謝謝補充。
# by Hina
超大量資料確實使用 Angular 會拖慢速度,我自己測試過 9MB 以上的 JSON(資料量 3K+,極端測試甚至有 20MB 的 JSON,資料筆數 8K 以上,在 Angular 要繪製到 DOM 的時候速度真的蠻慘的(使用 Chrome Canary 後來即便使用 React 也無法解決問題,實際上,超大量資料幾乎無關於用了什麼 Framework 或是 Library 了(攤手 問過 ng 與 react 這方面的高手,大部分的建議都是,用 Canvas 效能應該會好很多(苦笑
# by Jeffrey
to Hina, 八千筆資料 @@,夠猛!原本我也好奇react能否克服這種艱險,感謝先賢先烈(喂)的寶貴血淚經驗分享,已筆記。
# by pet
請問如果大量的資料( ex : 8mb) 來源是從restful api那邊透過ajax呼叫的方式取得, 那如果使用無線網路或是手機來使用application在資料取得上就會很慢. 有沒有辦法壓縮回傳回來的json data讓回傳大小減少?或是有更好的處理方式? 感謝黑大~~
# by Jeffrey
to pet, 實務上瀏覽器跟伺服器的HTTP傳輸多半會啟用內容壓縮,故實際傳遞的資料量已被壓縮過,且資料重覆性愈高,壓縮幅度愈大,你可以使用Fiddler之類的工具觀察,若壓縮後的尺寸已可接受,就不必傷腦筋了。面對大量陣列,我常用的另一種手法是不用JSON改用CSV(前題是陣列中的元素屬性一致)甚至自訂編碼格式,可以進一步提高資料密度減少體積,但程式較複雜且偏向非標準化,使用時機得拿捏。