【问题标题】:Knockout computed triggering multiple times on a single action if two dependence paths to changed observable如果两个依赖路径改变了 observable,则 Knockout 计算在单个操作上多次触发
【发布时间】:2019-03-29 14:57:14
【问题描述】:

我有一个绑定到 UI 元素的可观察 A。 我还有一个取决于 A 的计算 B。 我有一个取决于 A 和 B 的计算 С。 我订阅了 C

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

我认为原因是 A 有两个订阅: A: [B, C]
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.js


    【解决方案1】:

    当其依赖项之一发生变化时,observable 会重新计算。由于您在每次运行时都创建一个新对象,因此 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>

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

    解决此问题的另一种方法是添加自定义相等比较器。这让淘汰赛检查,当依赖项发生变化时,新结果是否实际上与前一个结果不同。只有当两者不同时,订阅者才会更新。

    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>

    两种方法的区别

    在延迟示例中,淘汰排序将计算的重新执行推送到setTimeout。它只会运行一次,但你不会知道“何时”。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-23
      相关资源
      最近更新 更多