JTangming / blog

My repository on GitHub.
Other
53 stars 0 forks source link

Angular 2之变化检测 #3

Open JTangming opened 8 years ago

JTangming commented 8 years ago

翻译的这篇文章中,我将深入探讨angular2的变化监测系统。

高级概述

一个angular2应用是一个组件树。

一个angular2应用是一个反应系统,变化监测是它的核心。

每一个组件都会有一个负责检查其模板中定义的绑定的更改检测器,例如这样的绑定:{{todo.text}}和[todo]=”t”。变化监测是由根到叶的深度优先顺序来传播绑定的。

Angular2没有一种通用的机制来实现数据的双向绑定(不过你任然可以实现数据绑定行为和ng-model,阅读这里了解更多)。这就是为什么上图的变化监测图是一颗定向的树并且不是循环的(也就是说是一种树形图),这使得系统的性能变现更好,更重要的是,我们将保障系统更易于预测和调试。

那它到底有多快呢?

默认情况下,变化检测通过树的每一个节点来检查它是否改变了,并且在每一浏览器事件都实现了它。尽管它看起来非常的低效,但在几毫秒内,Angular2变化监测系统能通过成百上千次简单的检查(次数依赖于不同的平台)。

因为在JavaScript语言中不提供给我们对象的变化通知,所以Angular必须保守的要每一次运行所有的检测。但是我们可能知道我们的某些可确定性的属性,例如使用不可改变的或可观察的对象,以前angular不能利用这些,但是现在可以。

不可变对象(IMMUTABLE OBJECTS)

如果一个组件仅仅只依赖于它的输入属性,并且它是不可变的,那么当其绑定属性变化时该组件也会发生改变,因此我们可以跳过该组件的子树变化监测,直到这样的一个属性事件发生变化。当事件发生时,我们能立马监测子树,然后禁用它,直到下一次的变化(灰色的框框代表禁用了变化监测)。

如果我们使用不可变对象,一个大的变化检测树大部分时间都是被禁用的。

实施这个监测是微不足道的,仅仅是设置了一个变化监测策略到Onpush这个方法上。

@Component({changeDetection:ChangeDetectionStrategy.OnPush})
class ImmutableTodoCmp {
  todo:Todo;
}

可观测对象(OBSERVABLE OBJECTS)

如果一个组件仅仅依赖于它的输入属性,并且它是可观测的,那么该组件会随着它的输入属性触发一个事件来改变。因此我们能跳过该组件变化监测树的子树直到这样一个事件被触发,当它发生改变,我们监测一次子树,并且禁用它直到下一次变化。

尽管表面上看它可能类似于不可变对象,但还是完全不同的。如果你有一个组件树使用不可变对象绑定,一个变化必须经历从根开始的所有组件检查,使用可观察对象则不会有这种情况。

让我举一个小例子演示这个问题

type ObservableTodo = Observable<todo>;
type ObservableTodos = Observable<array>>;

@Component({selector:'todos'})
class ObservableTodosCmp {
  todos:ObservableTodos;
  //...
}

ObservableTodosCmp模板为

<todo *ngFor="#t of todos" todo="t"></todo>

最后的ObservableTodosCmp为:

@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是绑定系统的数目。

这种能力不需要依赖于任何特殊的库,任何一个可观测的库的实现就是那么几行代码。

可观测对象会导致级联更新吗?

可观察到的对象有坏名声,因为它们会导致级联更新。任何有使用过依赖于可观测模块框架来构建大型应用的人都知道我在讲什么。一个可观测对象的更新能导致一串其它可观测对象触发更新,也在做同样的事情,沿途的视图也将会被更新,这样的系统是很难去debug的。

在Angular2中使用可观测的对象显然不会有这样的问题,一个可观测对象的事件触发仅仅是作为下次从当前目录到组件跟目录的监测,然后,通过节点树的深度优先的顺序启动正常的变化检测过程,因此更新的顺序不会因为你是否使用过可观测对象而改变,这是非常重要的。使用可观测对象成为了一种简单的性能优化但不会改变你对系统的认知。

我们是否必须到处使用可观测的和不可变的对象才能体现它的好处呢?

答案是否定的,你没必要这么做。你可以使用到你应用的某一处(例如某处一张巨大的表),这块的性能将会受益,更有甚者,你可以构建不同的组件来使他们的性能最优,举个例子,一个“可观测的组件”可以包含一个“不可变属性的组件”,该“不可变属性组件”本身可以包含一个“可观测的组件”,即使在这样的情况下,变化检测系统将减少所需的传输的数量的变化。

无特例

对不可变属性和可观测对象的支持不需要(baked into)变化监测,这种类型的组件不需要用一种特殊的方式来处理,所以你可以使用一种智能方式的变化监测来写你的指令,举个例子,想象一下你的内容每N秒更新一次。

总结

http://victorsavkin.com/post/110170125256/change-detection-in-angular-2

1inus commented 7 years ago

很强大,找了很久找到这,一时半会还消化不了