我有一个绑定(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
});
最佳答案
当其依赖项之一发生变化时,可观察对象会重新计算。由于您在每次运行时都创建了一个新对象,因此 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/