Knockout Performance - removeAll Observable Array

时间:2017-07-05 06:47:44

标签: javascript html knockout.js

在我的html视图中,我使用knockout viewmodel绑定一个html表,其中包含3个observableArrays,如下所示:

function myViewModel() {
    var self = this;
    self.RowIds = ko.observableArray();
    self.ColIds = ko.observableArray();

    self.allCellsList = ko.observableArray();

    self.calculateText = function (r_id, c_id) {
        console.log('calculateText');
        var item = ko.utils.arrayFirst(self.allCellsList(), function (i) {
            return i.rowId() == r_id && i.colId() == c_id;
        });
        return item.text();
    }

    self.loadData = function (rowIds, colIds, allCellsList) {

        //remove old data (not necessary, only for testing , to see how long it takes)
        self.allCellsList.removeAll();

        //populate new data:
        self.RowIds(ko.mapping.fromJS(rowIds));
        self.ColIds(ko.mapping.fromJS(colIds));
        self.allCellsList(ko.mapping.fromJS(allCellsList));
    }

}

RowIds包含表中每行的ID列表,CoIds包含表中每列的ID列表。 allCellsList是包含对象列表的主表,其中每个对象都包含两个标识符:rowId和colId。 在表中的每个单元格中调用函数calculateText,并通过这两个字段计算allCellsList中的匹配数据。 如下:

<table>
    <tbody data-bind="foreach:RowIds">
        <tr data-bind="foreach:$root.ColIds()">
            <td>
                <div data-bind="text:$root.calculateText($data , $parent)">
                </div>
            </td>
        </tr>
    </tbody>
</table>

当我使用loadData函数中的新数据初始化allCellsList时,通过删除旧数据并获取新数据(或直接获取新数据,而不使用removeAll函数) - 调用calculateText函数,对于每个项目在旧列表中,以及新列表中的每个项目。 如果旧列表包含大量记录 -​​ 清除数组操作需要太长时间。 (有时3-4秒)

我的问题是如何在这种情况下或在其他情况下填充allCellsList,以便重新加载操作会更快? 当清除allCellsList时,有没有办法避免调用函数calculateText?

谢谢。

1 个答案:

答案 0 :(得分:0)

调用calculateTextRowIds.removeAll时,ColIds.removeAll函数不应该运行。但是,将在调用self.allCellsList.removeAll运行,因为它对此数组有依赖性。

要只进行一次计算,请按以下顺序执行操作:

self.loadData = function (rowIds, colIds, allCellsList) {
    self.RowIds.removeAll();
    self.ColIds.removeAll();

    // You don't need this and I'd suggest you removing it
    // self.allCellsList.removeAll();

    // First load the data that `calculateText` depends on
    self.allCellsList(ko.mapping.fromJS(allCellsList));

    // Calculate for these only when added to DOM
    self.ColIds(ko.mapping.fromJS(colIds));
    self.RowIds(ko.mapping.fromJS(rowIds));

}

编辑,进一步说明我的观点:每当你清空vals时DOM中有数据,你就会得到每个单元格的重计...如果你确定有设置vals时,DOM中没有数据,每个单元格只能获得一个

var valSource = { 
  a: { "1": "A1", "2": "A2", "3": "A3" }, 
  b: { "1": "B1", "2": "B2", "3": "B3" }, 
  c: { "1": "C1", "2": "C2", "3": "C3" }
};
var colSource = [{ id: "a" }, { id: "b" }, { id: "c" }];
var rowSource = [{ id: "1"   }, { id: "2" }];

var cols = ko.observableArray([]);
var rows = ko.observableArray([]);
var vals = ko.observable({});
var i = 0;

var getCellValue = function(col, row) {  
  i = i + 1;
  var col = vals()[col.id] || {};
  return col[row.id];
};


var updateSlow = function() {
  // Clear
  vals({}); // Triggers a recalc for every cell
  rows([]); // Remove these freshly recalced cells...
  cols([]); // Does nothing
  
  // Set
  rows(rowSource.slice(0)); // Adds rows
  cols(colSource.slice(0)); // Adds cells and recalcs
  vals(Object.assign({}, valSource)); // Recalcs again
  
  console.log("Updates slow:", i);
  i = 0;
};

var updateFast = function() {
  // Clear
  rows([]);
  cols([]);
  
  // Set
  vals(Object.assign({}, valSource)); // No DOM, so no recalc
  rows(rowSource.slice(0)); // Add rows
  cols(colSource.slice(0)); // Add cells and calculate once

  console.log("Updates fast:", i);
  i = 0;
}

ko.applyBindings({ cols: cols, rows: rows, getCellValue: getCellValue, updateSlow: updateSlow});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<button data-bind="click: updateSlow">update slow</button>
<button data-bind="click: updateFast">update fast</button>

<table data-bind="foreach: rows">
  <tr data-bind="foreach: cols">
    <td data-bind="text: getCellValue($data, $parent)"></td>
  </tr>
</table>

相关问题