一次更改多个可观察对象时提高淘汰赛性能

Improve knockout performance when changing multiple observables at once

对于所提供的 JSFiddle 中 "click" 事件的任何性能建议,我将不胜感激。

这个想法是为了在一次更改多个 observable 时提高性能。

我找不到任何关于以批处理方式暂停和恢复更新通知的文档。

http://jsfiddle.net/g9jpxcm2/

$("#all").click(function(){
    var tasks = ko.dataFor($("#tasks")[0]).tasks(),
        checked = this.checked;

    //TODO: performance? Batch changes?
    for(var i = 0, l = tasks.length; i<l; i++){
         tasks[i].done( !!checked );
    }
});

专注于有效的优雅解决方案通常是个好主意,并且仅在遇到性能问题时进行优化,或者至少合理地预期特定代码片段会成为瓶颈。

本着这种精神,我编写了一个解决方案,使“全部”复选框的响应速度更快一些:

  1. 如果所有的任务框都被选中,则全部被选中
  2. 如果选中了所有任务框而未选中任务框,则将取消选中所有任务
  3. 选中全部会导致选中所有任务框
  4. 取消选中全部会导致取消选中所有任务框

没有点击事件处理,也没有使用 jQuery。尽管每次值更改时这都会贯穿任务项,但我认为您可以有一百个任务框并且没有明显的性能问题。

var viewModel = ko.mapping.fromJS({
tasks: [{
    name: "Task 1",
    done: false    
},{
    name: "Task 2",
    done: true    
},{
    name: "Task 3",
    done: false    
}]
});

viewModel.allChecked = ko.computed({
read: function () {
    var tasks = viewModel.tasks();
    for (var i=0; i<tasks.length; ++i) {
        if (!tasks[i].done()) {
            console.debug("Nope");
            return false;
        }
    }
    return true;
},
write: function (newValue) {
    var tasks = viewModel.tasks();
    for (var i=0; i<tasks.length; ++i) {
        tasks[i].done(newValue);
    }
}
});

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdn.rawgit.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js"></script>
<label>All</label>
<input id="all" type="checkbox" data-bind="checked:allChecked" />
<br><br><br>
<div id="tasks" data-bind="foreach: tasks">
<label data-bind="text: name"></label>
<input type="checkbox" data-bind="checked: done" />    
</div>

您可以更改模型以跟踪已检查任务的集合,而不是直接指示已检查哪些任务。它应该使实现更容易和更快,因为只有一个 observable 会被更新。只需创建一个(可观察的)数组并将 checked 绑定到该数组。还要确保 value 已绑定。

<div id="tasks" data-bind="foreach: tasks">
    <label data-bind="text: name"></label>
    <input type="checkbox" data-bind="checked: $parent.checkedTasks, value: $data" />
</div>
var data = {
    tasks: [{
        name: "Task 1"
    },{
        name: "Task 2"
    },{
        name: "Task 3"
    }]
};

ko.applyBindings( ko.mapping.fromJS(data, {}, {
    checkedTasks: ko.observableArray([])
}), $("#tasks")[0]);

$("#all").click(function(){
    var model = ko.dataFor($("#tasks")[0]),
        checked = this.checked;

    if (checked) {
        model.checkedTasks(model.tasks().slice(0));
    } else {
        model.checkedTasks([]);
    }
});

fiddle