面对淘汰映射插件的性能问题

时间:2012-09-06 17:02:58

标签: performance knockout.js knockout-mapping-plugin

我有大约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)

有解决方法吗?或者我应该选择网络工作者来改善表现?

4 个答案:

答案 0 :(得分:1)

Knockout.viewmodel是knockout.mapping的替代品,它可以更快地为像这样的大型对象数组创建视图模型。您应该注意到性能的显着提升。

http://coderenaissance.github.com/knockout.viewmodel/

答案 1 :(得分:0)

我遇到了映射插件的同样问题。 Knockout团队表示,映射插件不适用于大型阵列。如果您必须将大量数据加载到页面,那么您可能会对系统进行不正确的设计。

解决此问题的最佳方法是使用服务器分页,而不是在页面加载时加载所有数据。如果您不想更改应用程序的设计,可以使用一些可能对您有所帮助的解决方法:

  1. 手动映射数组:

    var data = storage.transactions();
    var mappedData = ko.utils.arrayMap(data , function(item){
        return ko.mapping.fromJS(item);
    });
    
    var transactionList = ko.observableArray(mappedData);
    
  2. 异步映射数组。我编写了一个函数,它按照另一个线程中的部分处理数组,并向用户报告进度:

    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();
    };
    
  3. 您的代码:

    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 。等

优点是:

  • 可自定义(比 knockout.mapping 更容易)。
  • 足够短,可以复制粘贴它,并根据需要为不同的对象编写单独的映射。
  • 您的网页中未包含较小的代码 knockout.mapping-latest.js
  • 应该更快,因为它只是绝对必要的。