Mik*_*sen 86 javascript performance knockout.js
我刚刚开始使用Knockout.js(一直想尝试一下,但现在我终于有了借口!) - 然而,当我将一个表绑定到一个相对较小的一组时,我遇到了一些非常糟糕的性能问题数据(约400行左右).
在我的模型中,我有以下代码:
this.projects = ko.observableArray( [] ); //Bind to empty array at startup
this.loadData = function (data) //Called when AJAX method returns
{
for(var i = 0; i < data.length; i++)
{
this.projects.push(new ResultRow(data[i])); //<-- Bottleneck!
}
};
Run Code Online (Sandbox Code Playgroud)
问题是for上面的循环大约需要30秒左右,大约需要400行.但是,如果我将代码更改为:
this.loadData = function (data)
{
var testArray = []; //<-- Plain ol' Javascript array
for(var i = 0; i < data.length; i++)
{
testArray.push(new ResultRow(data[i]));
}
};
Run Code Online (Sandbox Code Playgroud)
然后for循环在眨眼间完成.换句话说,pushKnockout observableArray对象的方法非常慢.
这是我的模板:
<tbody data-bind="foreach: projects">
<tr>
<td data-bind="text: code"></td>
<td><a data-bind="projlink: key, text: projname"></td>
<td data-bind="text: request"></td>
<td data-bind="text: stage"></td>
<td data-bind="text: type"></td>
<td data-bind="text: launch"></td>
<td><a data-bind="mailto: ownerEmail, text: owner"></a></td>
</tr>
</tbody>
Run Code Online (Sandbox Code Playgroud)
我的问题:
push每次调用它时都会进行一些重度重新计算,例如重建绑定的DOM对象.有没有办法延迟这个recalc,或者可能一次推入我的所有项目?如果需要,我可以添加更多代码,但我很确定这是相关的.在大多数情况下,我只是关注网站上的Knockout教程.
更新:
根据以下建议,我更新了我的代码:
this.loadData = function (data)
{
var mappedData = $.map(data, function (item) { return new ResultRow(item) });
this.projects(mappedData);
};
Run Code Online (Sandbox Code Playgroud)
但是,this.projects()400行仍需要大约10秒钟.我承认我不确定没有 Knockout 会有多快(只是通过DOM添加行),但我觉得它会比10秒快得多.
更新2:
根据下面的其他建议,我给了jQuery.tmpl一个镜头(KnockOut原生支持),这个模板引擎将在3秒内绘制大约400行.这似乎是最好的方法,缺少在滚动时动态加载更多数据的解决方案.
Jim*_* G. 50
请参阅:Knockout.js Performance Gotcha#2 - 操作observableArrays
更好的模式是获取对底层数组的引用,推送到它,然后调用.valueHasMutated().现在,我们的订阅者只会收到一个通知,表明该阵列已更改.
mad*_*kay 16
正如评论中所建议的那样.
Knockout拥有与(foreach,with)绑定相关联的自己的本机模板引擎.它还支持其他模板引擎,即jquery.tmpl.请阅读此处了解更多详情.我没有对不同的引擎进行任何基准测试,所以不知道它是否会有所帮助.阅读您之前的评论,在IE7中,您可能很难获得所追求的性能.
顺便说一句,KO支持任何js模板引擎,如果有人为它编写了适配器.您可能想尝试其他人,因为jquery tmpl将被JsRender取代.
Tim*_*ord 13
除了使用$ .map之外,还要使用KO 分页.
我使用1400个记录的大型数据集时遇到了同样的问题,直到我使用了敲除分页.使用$.map加载记录确实产生了巨大的差异,但DOM渲染时间仍然很可怕.然后我尝试使用分页,这使我的数据集照明快速 - 以及更加用户友好.页面大小为50使得数据集的压倒性更大,并且显着减少了DOM元素的数量.
它很容易用KO做:
http://jsfiddle.net/rniemeyer/5Xr2X/
del*_*ree 11
KnockoutJS有一些很棒的教程,特别是有关加载和保存数据的教程
在他们的情况下,他们使用getJSON()非常快的数据来提取数据.从他们的例子:
function TaskListViewModel() {
// ... leave the existing code unchanged ...
// Load initial state from server, convert it to Task instances, then populate self.tasks
$.getJSON("/tasks", function(allData) {
var mappedTasks = $.map(allData, function(item) { return new Task(item) });
self.tasks(mappedTasks);
});
}
Run Code Online (Sandbox Code Playgroud)
避免在渲染非常大的数组时锁定浏览器的解决方案是“限制”数组,以便一次仅添加几个元素,并且在两者之间进行睡眠。这是一个将执行此操作的函数:
function throttledArray(getData) {
var showingDataO = ko.observableArray(),
showingData = [],
sourceData = [];
ko.computed(function () {
var data = getData();
if ( Math.abs(sourceData.length - data.length) / sourceData.length > 0.5 ) {
showingData = [];
sourceData = data;
(function load() {
if ( data == sourceData && showingData.length != data.length ) {
showingData = showingData.concat( data.slice(showingData.length, showingData.length + 20) );
showingDataO(showingData);
setTimeout(load, 500);
}
})();
} else {
showingDataO(showingData = sourceData = data);
}
});
return showingDataO;
}
Run Code Online (Sandbox Code Playgroud)
根据您的用例,这可能会导致UX的大幅改进,因为用户可能只需要在滚动之前看到第一批行。
在我的案例中,利用push()接受变量参数的性能最佳。1300行加载了5973毫秒(〜6秒)。通过这种优化,加载时间减少到914ms(<1秒)。
改进了84.7%!
有关将项目推送到observableArray的更多信息
this.projects = ko.observableArray( [] ); //Bind to empty array at startup
this.loadData = function (data) //Called when AJAX method returns
{
var arrMappedData = ko.utils.arrayMap(data, function (item) {
return new ResultRow(item);
});
//take advantage of push accepting variable arguments
this.projects.push.apply(this.projects, arrMappedData);
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
43852 次 |
| 最近记录: |