我有大约1100条记录的大量数据集。此数据集映射到可观察数组,然后绑定到视图。由于这些记录经常更新,因此每次使用ko.mapping.fromJS帮助程序更新可观察数组。
此特定命令大约需要40秒来处理所有行。用户界面只锁定了那段时间。
这是代码 -
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
ko.mapping.fromJS(data,transactionList)
有解决方法吗?或者我应该选择网络工作者来改善表现?
答案 0 :(得分:1)
Knockout.viewmodel是knockout.mapping的替代品,它可以更快地为像这样的大型对象数组创建视图模型。您应该注意到性能的显着提升。
答案 1 :(得分:0)
我遇到了映射插件的同样问题。 Knockout团队表示,映射插件不适用于大型阵列。如果您必须将大量数据加载到页面,那么您可能会对系统进行不正确的设计。
解决此问题的最佳方法是使用服务器分页,而不是在页面加载时加载所有数据。如果您不想更改应用程序的设计,可以使用一些可能对您有所帮助的解决方法:
手动映射数组:
var data = storage.transactions();
var mappedData = ko.utils.arrayMap(data , function(item){
return ko.mapping.fromJS(item);
});
var transactionList = ko.observableArray(mappedData);
异步映射数组。我编写了一个函数,它按照另一个线程中的部分处理数组,并向用户报告进度:
function processArrayAsync(array, itemFunc, afterStepFunc, finishFunc) {
var itemsPerStep = 20;
var processor = new function () {
var self = this;
self.array = array;
self.processedCount = 0;
self.itemFunc = itemFunc;
self.afterStepFunc = afterStepFunc;
self.finishFunc = finishFunc;
self.step = function () {
var tillCount = Math.min(self.processedCount + itemsPerStep, self.array.length);
for (; self.processedCount < tillCount; self.processedCount++) {
self.itemFunc(self.array[self.processedCount], self.processedCount);
}
self.afterStepFunc(self.processedCount);
if (self.processedCount < self.array.length - 1)
setTimeout(self.step, 1);
else
self.finishFunc();
};
};
processor.step();
};
您的代码:
var data = storage.transactions();
var transactionList = ko.observableArray([]);
processArrayAsync(data,
function (item) { // Step function
var transaction = ko.mapping.fromJS(item);
transactionList().push(transaction);
},
function (processedCount) {
var percent = Math.ceil(processedCount * 100 / data.length);
// Show progress to the user.
ShowMessage(percent);
},
function () { // Final function
// This function will fire when all data are mapped. Do some work (i.e. Apply bindings).
});
您也可以尝试其他映射库:knockout.wrap。它应该比映射插件更快。
我选择了第二个选项。
答案 2 :(得分:0)
我还想到了一个解决方法如下,这使用了更少的代码 -
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
// Instead of - ko.mapping.fromJS(data,transactionList)
var i = 0;
//clear the list completely first
transactionList.destroyAll();
//Set an interval of 0 and keep pushing the content to the list one by one.
var interval = setInterval(function () {if (i == data.length - 1 ) {
clearInterval(interval);}
transactionList.push(ko.mapping.fromJS(data[i++]));
}, 0);
答案 3 :(得分:0)
映射不是魔术。在大多数情况下,这个简单的递归函数就足够了:
function MyMapJS(a_what, a_path)
{
a_path = a_path || [];
if (a_what != null && a_what.constructor == Object)
{
var result = {};
for (var key in a_what)
result[key] = MyMapJS(a_what[key], a_path.concat(key));
return result;
}
if (a_what != null && a_what.constructor == Array)
{
var result = ko.observableArray();
for (var index in a_what)
result.push(MyMapJS(a_what[index], a_path.concat(index)));
return result;
}
// Write your condition here:
switch (a_path[a_path.length-1])
{
case 'mapThisProperty':
case 'andAlsoThisOne':
result = ko.observable(a_what);
break;
default:
result = a_what;
break;
}
return result;
}
上面的代码在对象层次结构的任何级别从 mapThisProperty 和和AlsoThisOne 属性创建可观察对象;其他属性保持不变。您可以使用 a_path.length 表示值所处的级别(深度),或使用 a_path 的更多元素来表达更复杂的条件。例如:
if (a_path.length >= 2
&& a_path[a_path.length-1] == 'mapThisProperty'
&& a_path[a_path.length-2] == 'insideThisProperty')
result = ko.observable(a_what);
您可以在条件中使用 typeOf a_what ,例如使所有字符串都可观察。 您可以忽略某些属性,并在特定级别插入新属性。 或者,您甚至可以省略 a_path 。等
优点是: