接獲使用者反應,某個使用KendoGrid顯示大量資料的網頁,用IE檢視的話速度慢到嚇人。聞此言,馬上打開IE11測試,果真嚇得我差點閃了兩滴… 也太慢了吧!
先交代問題情境,專案使用Angular搭配Kendo UI開發,順理成章使用Kendo UI提供的Angular Directive,寫成<div kendo-grid …>,資料筆數偏多,大約一千多到兩千筆,基於KendoGri的強大彈性及高複雜度,處理起來多耗點時間倒也合理,但這個案例在IE的表現與期望出入頗大,值得調查。
修改KendoGrid的官方範例,我設計了一個實驗:
<divid="example"ng-app="KendoDemos">
<divng-controller="MyCtrl">
<buttonng-click="loadData()">Load Data</button>
<spanng-bind="duration"></span>
<divkendo-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,效率應會提升。所以我又寫了對照組:
<divid="example">
<div>
<button>Load Data</button>
<spanid="duration"></span>
<divid="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時也將面臨相同挑戰,這點留待日後驗證。