1. Change Detection
angular 使用了 https://github.com/angular/zone.js 来监视 component 的变更,并在需要的时候 render dom。由于 ng 2 的 component 的变更检测永远是从 root 开始,一直到最底层的 component 结束,大概像下图这样(图中 CD 表示 change detection):
所以在一个正常的 ng2 change detection cycle 中,不管哪一个 component 中的 ngZone 捕获到了一个异步事件的发生,就会从根 component 往下遍历,进行变更检测,直到最后一个 component。这个思路其实和 ng 1是非常类似的,只不过 ng 1的变更是双向的,也就是说在 ng 1中任意一个 directive/controller 的 $scope 在 $digest 过程中都可能会引发另一个 $scope 的变更从而让 $digest give up 掉重新再来一次。而在 ng2 的 change detection 中,子 component 如果在 change detection cycle 中有任意变更它的父 component 的行为,都会抛出异常从而终止。也就是说同样是 $digest cycle,在 ng 2 中变成了单向的可预测的:
2. 优化
由上面的部分我们可以知道,任意一次数据变更的代价是 time = C * N
C 表示平均每个绑定到模版的值变更检测所需的时间
N 表示绑定的数量。
在 ng2 中,由于框架已经自动为模版生成的代码做了非常多的优化,即使是在未使用优化过的 model 的情况下都已经可以达到 ng 1脏检测性能的 3-10 倍(同样绑定数量,同样检测次数)。视频中提到了这是因为 ng 2 在生成模版代码时,会动态生成让 js 引擎易于优化的代码,大概原理就是保持每次 check change 前后对象“形状 ”的一致。而如果在性能有瓶颈的地方,可以使用下面两种方式进行高阶优化:
Immutable
使用 immutable 的思路是减小 N 的值。我们知道在一个 component 中,绑定到模版上的数据一般都是对象上的属性,而对象的 check change 是非常昂贵的,需要遍历绑定到模版上的每一个值。而如果使用了 immutable,则这个 check 的过程将会非常迅速。并且,额外的好处是,在你告诉了框架你使用了 immutable 之后,当你的值并没有变化时,ng 2 的 change detection cycle 会忽略掉这个 component,如下图:
假设每个 component 都使用了 immutable model ,白色的部分是变更的部分,则在一个 change detection cycle 中只会 recheck & render 白色的部分,从而大大减少处理变更的代价。
你可以通过设置 changeDetection 来告诉框架只有绑定的这个对象引用改变时才处理变动:
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
class TodoCmp {
todo: Todo
}
Observable
这个通常是使用 Rx ,但也可以是 Backbone/Knockout 等(本人强烈不推荐)。使用这种模式的时候我们面临的情况和使用 immutable 的时候并不一样。使用 immutable 时 change detection cycle 的情况和使用 POJO 时很类似,变更非常自然的从 root component 开始往下,依次检测。而使用 Observable 时,情况就不一样了,它的变更很可能是从一个非常下层的子 component 中开始发生的,比如:
在图中一个子 component 通过 observable 观察到了一次数据的变更。这个时候我们需要告知 ng 2 这个部分发生了变更,它将会把这个 component 与它的父 component 一直到 root component 标记出来,并单独检测这一部分的变更:
我们可以使用同样的方法来设置这种变更检测:
@Component({
template: '{{counter}}',
changeDetection: ChangeDetectionStrategy.OnPush
})
class CartBadgeCmp {
@Input() addItemStream:Observable
counter = 0;
ngOnInit() {
this.addItemStream.subscribe(() => {
this.counter++; // application state changed
})
}
}
三种方案是可以混用的(其实 immutable 和 observable 在框架看来是一样的)。
比如:
只需要在特定的 component 标记出对应的 changeDetection。