【问题标题】:Improve knockout performance when changing multiple observables at once一次更改多个可观察对象时提高淘汰赛性能
【发布时间】:2015-07-20 14:32:04
【问题描述】:

如果对提供的 JSFiddle 中的“点击”事件提供任何性能建议,我将不胜感激。

这个想法是在一次更改多个可观察对象时提高性能。

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

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 );
    }
});

【问题讨论】:

  • 不相关,但你为什么不在这里使用click 绑定处理程序?
  • @Retsam - 在这个例子中恰好是这样 - 可以使用点击绑定 - 但是它对我有进一步的帮助吗?
  • @Stevanicus 很好,但在初始检查/取消检查时会有轻微的性能问题。在此处检查控制台以获取时间日志jsfiddle.net/g9jpxcm2/2。以后的请求将不会花费任何时间,因为订阅的维护(直到您手动进行才被处理)和最初的时间延迟,因为(订阅构建).cheers
  • 您遇到了什么样的性能问题?
  • 其实没有任何性能问题,只是想知道有没有办法优化它

标签: javascript jquery knockout.js observable batch-updates


【解决方案1】:

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

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

  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>

【讨论】:

  • 很好的答案,谢谢。正是我想要的
【解决方案2】:

您可以更改模型以跟踪检查任务的集合,而不是直接指示检查了哪些任务。它应该使实现更容易和更快,因为只有一个 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

【讨论】:

  • 感谢您的回答。我对每个任务都有可观察的,因为它是我从服务器接收数据的方式。因此,尽管我可以像您在 applyBindings 方法调用中所做的那样之后更改视图模型,但我仍然需要以与传入数据相同的方式将数据发送回服务器。
  • 我认为如果你在与服务器通信时生成数据会更好的整体体验。一般来说,模型中的约束和/或依赖的可观察量越少,当值发生变化时重新计算的次数就越少。如果它是生成的,那么您只需在传输数据时支付该费用,而不是在点击时支付,至少理论上是这样。
  • 是的,问题是虽然在这种情况下它可能是复选框......但理论上它可能是完全通用和动态的。在这种情况下,映射的 observable “完成”可以映射到任何嵌套对象中的任何值,所以我想尽可能少地接触视图模型。很简单,获取数据,无论它是什么,显示它,允许某种交互,然后发回。
  • 本质上你可能是对的,因为这也是 Knockout 在他们的例子中推荐它的方式,不幸的是它不适合我的情况:)
猜你喜欢
  • 2018-03-30
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 2014-08-08
  • 2017-09-19
  • 2014-11-05
  • 2012-09-08
相关资源
最近更新 更多