在我的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?
谢谢。
答案 0 :(得分:0)
调用calculateText
和RowIds.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>