依赖跟踪如何工作的(How dependency tracking works)

时间:2021-08-04 15:48:38

这一块主要是理论的讲解,本人刚接触这块不久,就不敢翻译了,请参见园子里其他人的现有文章 http://www.cnblogs.com/TomXu/archive/2011/11/22/2256820.html

文章内容:

新手没必要知道太清楚,但是高级开发人员可以需要知道为什么依赖监控属性能够自动跟踪并且自动更新UI…

事实上,非常简单,甚至说可爱。跟踪的逻辑是这样的:

  1. 当你声明一个依赖监控属性的时候,KO会立即调用执行函数并且获取初始化值。
  2. 当你的执行函数运行的时候,KO会把所有需要依赖的依赖属性(或者监控依赖属性)都记录到一个Log列表里。
  3. 执行函数结束以后,KO会向所有Log里需要依赖到的对象进行订阅。订阅的callback函数是重新运行你的执行函数。然后回头重新执行上面的第一步操作(并且注销不再使用的订阅)。
  4. 最后KO会通知上游所有订阅它的订阅者,告诉它们我已经设置了新值。

所有说,KO不仅仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来说,你的依赖属性可以是动态的:依赖属性A代表你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才重新执行。你不需要再声明其它的依赖:运行时会自动探测到的。

另外一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,如果模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。嵌套的模板也是自动的:如果模板X render模板 Y,并且Y需要显示监控属性Z的值,当Z改变的时候,由于只有Y依赖它,所以只有Y这部分进行了重新绘制(render)。

使用peek控制依赖(Controlling dependencies using peek)

(注:peek的具体作用还没用到,还在做一些有针对性的场景做验证,目前按文档的了解,可能就是防止循环引用时的作用比较大)

knockout会对依赖进行自动跟踪,但有时可能并不能达到我们的效果,我们可能需要手动控制哪一个依赖需要更新(我还没碰到过这种场景),尤其是当computed observable有一系列动作时,例如Ajax请求。peek函数可以访问observable 或者 computed observable而不用创建依赖。

下面的例子,computed observable用来使用Ajax方式重新加载currentPageData,它依赖两个observable 属性。当pageIndex改变时computed observable就会更新,但是会忽略selectedItem的改变,因为访问它使用了peek. 在这个示例中,selectedItem可能仅是在数据重新加载时做跟踪用的(即是:只使用它的值,但是它的值改变时,并不会重新加载数据(待验证))。

ko.computed(function() {
var params = {
page: this.pageIndex(),
selected: this.selectedItem.peek()
};
$.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);

注:可以使用extender扩展,达到阻止computed observable更新过于频繁的效果。

注:为什么循环依赖没有意义

computed observable应该是有许多的observable输入,但是只有一个值输出。因此,不应该在你的依赖链中出现循环。循环和递归是不一样的,它比较像Excel里两个单元格相互引用。它会导致无限循环。

当发生这种情况时knockout是怎么做的呢?它是这样来避免这种情况发生的:当已经在计算时knockout不会重新进行下次计算。这不会影响到你的代码。这有两种情况:当两个computed observables互相依赖时(可能其中一个或两个使用了deferEvaluation选项),或者一个computed observable向另一个它依赖的observable定入时(或者是直接的,也可能是通过依赖链有关系)。如果你出现这种情况,又需要避免循环依赖,就需要使用peek函数。