knockout.js - 如果两个依赖路径改变了 observable,则 knockout 计算会在单个 Action 上多次触发

标签 knockout.js

我有一个绑定(bind)到 UI 元素的可观察 A。 我还有一个依赖于 A 的计算 B。 我有一个计算出的 С,它同时依赖于 A 和 B。 我订阅了 C

当 UI 元素中的值发生变化时,会计算两次并调用两次订阅。

我认为原因是A有两个订阅: 答:[乙,丙]
Knockout 通知 B 关于 A 的变化。
在 B 被评估后,它通知 C 关于 B 的变化
然后它回到开始并调用 A 的第二个订阅,即 C。
这里我们有两个 C 调用。

有什么办法可以避免这种情况吗?

var viewModel = {
    firstName: ko.observable("Andrew"),
    lastName: ko.observable("King"),
};

viewModel.fullName = ko.computed(function() {
    return viewModel.firstName() + " " + viewModel.lastName();
});

viewModel.user = ko.computed(function() {
    return {
    fullName: viewModel.fullName(),
    lastName: viewModel.lastName()
  };
});

viewModel.user.subscribe(function() {
    // This is called once if I change first name
    // It is called twice if I change last name
});

http://jsfiddle.net/jngxwf5v/

最佳答案

当其依赖项之一发生变化时,可观察对象会重新计算。由于您在每次运行时都创建了一个新对象,因此 knockout 无法判断是否确实发生了变化。

使用延迟计算修复它

为了防止它在两个依赖项都发生变化时运行多次,您可以将其延迟:

var viewModel = {
    firstName: ko.observable("Andrew"),
    lastName: ko.observable("King"),
};

viewModel.fullName = ko.computed(function() {
    return viewModel.firstName() + " " + viewModel.lastName();
});

viewModel.user = ko.computed(function() {
    return {
    fullName: viewModel.fullName(),
    lastName: viewModel.lastName()
  };
}).extend({ deferred: true });

viewModel.user.subscribe(function(user) {
  console.log(user);
});

viewModel.firstName("John");
viewModel.lastName("Doe");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

使用自定义相等比较器修复它

解决此问题的另一种方法是添加自定义相等比较器。这让 knockout 检查,当依赖关系发生变化时,新结果是否真的与前一个不同。仅当两者不同时,订阅者才会更新。

var viewModel = {
    firstName: ko.observable("Andrew"),
    lastName: ko.observable("King"),
};

viewModel.fullName = ko.computed(function() {
    return viewModel.firstName() + " " + viewModel.lastName();
});

viewModel.user = ko.computed(function() {
  return {
    fullName: viewModel.fullName(),
    lastName: viewModel.lastName()
  };
});

viewModel.user.equalityComparer = (x, y) => x === y || x.fullName === y.fullName && x.lastName === y.lastName;

viewModel.user.subscribe(console.log);

viewModel.lastName("Doe");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

两种方法的区别

在延迟示例中, knockout 将计算的重新执行推到 setTimeout。它只会运行一次,但您不会知道“什么时候”。

在第二个例子中,计算函数被调用了两次(和之前一样)。唯一的区别是订阅者不会收到通知,因为这两个结果被认为是相等的。

关于knockout.js - 如果两个依赖路径改变了 observable,则 knockout 计算会在单个 Action 上多次触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55420169/

相关文章:

javascript - knockoutjs 的数据加载模式

knockout.js - 将 knockoutjs 对象移动到弹出编辑表单中

knockout.js - 如何在点击事件中访问 observableArray?

javascript - Knockout.js - 如何创建类似于 'with' 的自定义绑定(bind)处理程序

javascript - 如何在向函数发送参数时删除单击的行?昏死

knockout.js - knockout js : foreach binding adding a static element

javascript - 在knockoutJs中使用父ID的嵌套菜单

internet-explorer - Sammy.js 路由未从 IE10 中的 Knockout-bound 链接触发

javascript - 重新创建时 knockout PureCompulated 调用订阅者

javascript - Knockout JS 模板 HTML 无法正确呈现