4

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

  1. 基于用户事件,父组件更新子组件属性
  2. 此更新会在子组件中触发一个事件,该事件会更新父组件上的属性
  3. 父属性更新触发另一个更新子组件的事件
  4. ...

据我了解,Angular 1 中的类似情况是通过对这些相互依赖的属性触发的周期数设置限制来解决的,这会导致框架抛出错误。

Angular 2 是如何解决的?上例中的哪一点实际触发了变更检测?

4

3 回答 3

9

我读过 Angular 2 更改检测...在单次通过后变得稳定

Angular 2 并不“稳定”。对于 Angular 2 应用程序,我们负责编写应用程序,使其在单次通过后始终保持稳定。

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

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

Angular 2 中不允许出现副作用。关于您的问题,因此子组件不得因输入属性传播而修改父属性。因此,您可以说 Angular 2 通过不允许它“解决”了您所询问的情况。

这并不像听起来那么糟糕。我知道输入属性传播可以更改父属性的唯一方法是子组件是否为输入属性实现了一个 setter 方法,该方法修改了父组件在其模板中显示的另一个属性。(这是一个做这个的老笨蛋——见@Input set backdoor()方法。)通常你不会这样做。如果您确实需要这样做,那么 Günter 的评论就是:在 a 内进行更改setTimeout(),因此它将成为下一个更改检测周期的一部分。

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

因此,如果您在事件处理程序中进行更改,没问题。只有当你的二传手做了一些奇怪的事情时才会有问题。

于 2016-06-04T15:47:34.650 回答
3

在 devMode 中,变化检测会连续两次。如果第 2 回合识别出更改,则抛出。因此变化检测本身不能引起模型的变化。

Angular2 更改检测仅从父级更新到子级。因为变更检测本身不能引起变更,所以当所有变更都传播到叶子节点时,变更检测就完成了。

当一个事件或任何其他异步调用发生时,它会被完全处理,也可能导致从孩子到父母的变化(输出)。完成后,更改检测一次从根传播到叶子。

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

于 2016-06-04T08:53:25.867 回答
0

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

文章Angular 应用程序在单个更改检测周期后变得稳定吗?用说明性的例子解释了这一点。

于 2018-08-17T06:13:34.687 回答