Angular 2变化检测 - 组件之间的循环依赖如何解决?

时间:2016-06-04 08:32:58

标签: angular angular2-changedetection

我已经读过Angular 2变化检测是单向的,从组件树的顶部到底部,并且在单次传递后它变得稳定,这意味着没有多个变化周期检测。鉴于这些假设,在我们有父和子组件具有相互依赖属性的情况下会发生什么?例如:

  1. 基于用户事件,父组件更新子组件属性
  2. 此更新会在子组件中触发更新父项
  3. 上的属性的事件
  4. 父属性更新会触发另一个更新子组件的事件
  5. ...
  6. 根据我的理解,Angular 1中的类似情况通过设置对这些相互依赖的属性触发的循环次数的限制来解决,这将导致框架抛出错误。

    Angular 2如何解决?在上面的例子中,在哪一点上实际触发了变化检测?

3 个答案:

答案 0 :(得分:7)

  

我已经读过Angular 2变化检测......一次传递后变得稳定

Angular 2没有“稳定”。使用Angular 2应用程序,我们负责编写应用程序,使其在一次通过后始终保持稳定。

默认情况下(例如,您没有在任何组件上使用OnPush更改检测策略,也没有detach()任何组件),更改检测的工作方式如下:

  • Zone.js猴子修补的异步事件触发 - 例如,(click)事件,XHR响应,setTimeout()计时器。与该事件关联的回调运行,这可以更改我们的应用程序中的任何视图或应用程序数据。然后,由于猴子补丁,角度变化检测运行。换句话说,默认情况下(例如,您不是manually triggering change detection),只有猴子修补的异步事件会触发更改检测。
  • 从根组件开始,遍历组件树(深度优先遍历),检查每个数据绑定的变化。如果发现更改,则“传播”更改。根据模板绑定类型,传播可以
    • 将更改的值传播到DOM。例如,当使用{{}}绑定时,新值将传播到相应DOM元素的textContent属性。
    • 将更改的值传播到子组件。例如,当使用输入属性绑定([childInputProperty]="parentProperty")时,新值将传播到子输入属性。
  • 如果您处于开发模式,则会再次检查所有组件,但会发生无传播。第二次脏检查有助于我们发现代码存在问题,例如,如果我们违反idempotent rule,这是一种奇特的说法,即我们的一个绑定(其模板表达式)有副作用。换句话说,额外的开发模式检查让我们知道我们的代码在单次传递后是否不稳定。

Angular 2中不允许出现副作用。在引用您的问题时,子组件因此不能因输入属性传播而修改父属性。所以,你可以说Angular 2通过不允许它来解决你所问的情况。

这并不像听起来那么糟糕。我知道输入属性传播可以更改父属性的唯一方法是,子组件为input属性实现setter方法,该方法修改父对象在其模板中显示的另一个属性。 (这是执行此操作的old plunker - 请参阅@Input set backdoor()方法。)通常情况下,您不会这样做。如果您确实需要这样做,那么Günter的评论就会出现:在setTimeout()内进行更改,因此它将成为下一个更改检测周期的一部分。

我想再强调:事件处理程序在更改检测之前运行,因此他们可以自由更改应用程序中的任何数据 - 本地/组件视图数据,应用程序数据等等。因此,在事件处理程序中,子组件可以自由更改父数据。例如,假设父和子都具有对同一数组的引用。当事件处理程序运行时,父组件和/或子组件可以修改该数组。

因此,如果您在事件处理程序中进行更改,则没有问题。如果你的二传手做了奇怪的话,那就只有一个问题。

答案 1 :(得分:2)

在devMode中,变化检测连续两次转动。如果第二个转弯识别出它发生的变化。因此,更改检测本身不得导致模型的更改。

Angular2更改检测仅从父级更新为子级。由于更改检测本身不得导致更改,因此在将所有更改传播到叶节点上时,将完成更改检测。

当事件或任何其他异步调用发生时,它会被完全处理,并且还会导致从子节点到父节点(输出)的更改。完成此操作后,更改检测一旦从根传播到叶子。

这样Angular2变化检测可以避免循环。

答案 2 :(得分:0)

确实,更改检测是在一次通过中执行的,但是单个事件之后可能会有多个周期。

Does Angular applications become stable after a single change detection cycle?文章通过举例说明了这一点。