原文地址:http://victorsavkin.com/post/110170125256/change-detection-in-angular-2
在本文中,我将深入讨论Angular 2变化检测系统。
高级概述(HIGH-LEVEL OVERVIEW)
Angular 2 的应用组件是一个树形结构的。
Angular 2 应用是一个是反应系统,变化检测是它的核心。
每个组件都有一个变化检测器负责检查其模板中定义的绑定。如{{todo.text}}和[todo]=”t”绑定。绑定的变化检测通过是从根到叶的顺序传播的。
Angular 2 不具有执行双向数据绑定的一个通用机制(你仍然可以实现双向数据绑定行为和ng-model)。这就是为什么变化检测图是一个有向树,不能有循环(即。这是一个树状)。这使得系统性能更好。而更重要的是可以保证系统更可预测和更容易推理。
它有多快?
默认情况下,变化检测经过树的每个节点检测是否改变,它适用于每个浏览器。尽管它可能看起来非常低效,但在几毫秒内angular 2可以通过成千上万的简单检查(数量是依赖于平台)。
因为JavaScript语言不能提供给我们对象的变化通知,所以Angular必须每一次保守的运行所有的检测。当然现在我们可以使用某种特性来提高性能,例如,使用不可变(immutable)或者可观察对象(observable objects),先前的Angular无法利用这一优势,但现在可以用。
不可变对象(IMMUTABLE OBJECTS)
如果一个组件只取决于它绑定的属性,并且绑定的属性是不可变的对象,则当其绑定对象变化时该组件也会发生改变。因此在变化检测树中我们可以跳过该组件的子树,直到这样的事件发生(对象发生变化)。当事件发生时,我们可以检查一次树,然后禁用它,直到下一个变化(灰色框表示禁用变化检测)。
如果我们使用不可变对象,所述变化检测树的一大块大部分时间将被禁用。
实现这个功能实在是微不足道。只要设置变化检测策略为ON_PUSH。
@Component({changeDetection:ON_PUSH})
class ImmutableTodoCmp {
todo:Todo;
}
可观察对象(OBSERVABLE OBJECTS)
如果一个组件只依赖于它的绑定,并且绑定是可观察的,如果这个绑定发生变化发出事件通知这个组件改变,因此在变化检测树中我们可以跳过该组件的子树,直到这样的事件(观察者发出通知)发生,当事件发生时,我们可以检查一次树,然后禁用它,直到下一个变化(灰色框表示禁用变化检测)。
这个听起来类似于不可变对象,但这是完全不同的,如果你有一个组件树使用不可变对象绑定,一个变化必须经历从根开始的所有组件检查,观察者则不会又这种情况。
用一个小例子来展示这个问题
type ObservableTodo = Observable<Todo>;
type ObservableTodos = Observable<Array<ObservableTodo>>;
@Component({selector:’todos’})
class ObservableTodosCmp {
todos:ObservableTodos;
//...
}
ObservableTodosCmp 模板
<todo *ng-for=”var t of todos” todo=”t”></todo>
ObservableTodoCmp
@Component({selector:’todo’})
class ObservableTodoCmp {
todo:ObservableTodo;
//...
}
正如你所看到的,这里Todos组件只引用到一个可观察的todo数组。所以看不到Todo的变化。
当被观察的todo发生改变触发一个事件,变化检测系统将检查从根的路径到那个改变的Todo组件
如果我们的应用只使用可观察对象,当它启动时,Angular将检查所有对象。
所以第一遍检查后的状态将如下所示。
比方说第一个TODO发生变化观察者触发一个事件。该系统将切换到以下状态:
在检查App_ChangeDetector,Todos_ChangeDetector和Todo_ChangeDetector后它会回到这种状态。
假设很少发生变化,形成一个平衡的树组件,使用可观察者对象的变化检查的复杂度从O(N)到O(logN),其中,N是系统中的绑定的数量。
这种能力是不依赖于任何特定的库,简单的可观察对象实现只是几行代码的事。
可观察对象会引发级联更新吗?
可观察对象有不好的名声,因为它们会导致级联更新,任何拥有依赖于可观察对象模型框架构建大型应用的经验的人都知道我在说什么,一个可观察到的对象更新会导致一群其他可观察对象触发更新,做同样的事情。触发的一路上某处视图将被更新,这样的系统是非常难推理。
使用Angular 2的可观察对象,如上图所示将不会有这个问题,观察者通过触发事件只是标志着下一次需要检查的路径,然后通过节点树的深度优先的顺序启动正常的变化检测过程,因此,更新的顺序不会因为是否使用观察而改变。这是非常重要的。使用可观察对象将成为一个简单的优化性能方式,但不会改变您对系统思考方式。
可观察对象或者不可变对象混用?
您可以在您的应用程序的一部分使用可观察对象(例如,在一些巨大的表),这部分将获得性能优势。甚至更多,你可以撰写不同类型的组件,例如,一个“可观察组件”可以包含一个“不可变对象组件”,其本身可以包含一个“可观察对象组件”。即使在这种情况下变化检测系统将减少检查需要传播的数量变化。