【问题标题】:Angular 4 detach change detection between childrenAngular 4子项之间的分离变化检测
【发布时间】:2018-01-18 19:56:26
【问题描述】:

我不明白为什么即使我使用 ChangeDetectionStrategy.OnPush 和 changeDetectorRef.detach() 函数 ngDoCheck 仍然被调用。 我的应用中有数千个组件,如果从 child2 引发事件(鼠标单击等),我想阻止 child1 的 changeDetection。

这是plunker

如你所见,我有一个父组件

@Component({
  selector: 'my-app',
  template: `     
    <app-child1 test="test"></app-child1>
    <app-child2 test="test"></app-child2> `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  test = 'test';

constructor() { }

  ngDoCheck() {
    console.log("### ngDoCheck app component");
  }
}

和 2 个相同的孩子:

@Component({
  selector: 'app-child1',
  template: `<input type="text" [(ngModel)]="test" /><br/> `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1Component implements OnInit {
  @Input()
  test: string;

  constructor(public cd: ChangeDetectorRef) { }

  ngAfterViewInit() {
      console.log("###### DETACH child 1");
      this.cd.detach();

  }

  ngDoCheck() {
    console.log("### ngDoCheck child 1");
  }
}

如果我开始在 child1 的输入中输入,就会调用 child2 的 ngDoCheck 函数。

想想有成千上万的孩子,它变得非常缓慢......

谢谢!

【问题讨论】:

标签: angular angular2-changedetection


【解决方案1】:

这是预期的行为,请阅读Everything you need to know about change detection in Angular。这是报价:

  1. 在子组件上调用 OnInit 和 ngDoCheck(仅在第一次检查时调用 OnInit)
  2. 为子视图运行更改检测(重复此列表中的步骤)

如您所见,ngDoCheck 总是在子组件上触发。 OnPush 检查在尝试为子组件运行更改检测时之后执行。这是报价:

最后,对当前视图的变化检测负责 启动子视图的更改检测(操作 8)。这是 检查子组件视图状态的地方,如果它是 ChecksEnabled,然后为此视图执行更改检测。 以下是相关代码:

viewState = view.state;
...
case ViewAction.CheckAndUpdate:
  if ((viewState & ViewState.ChecksEnabled) &&
    (viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
    checkAndUpdateView(view);
  }
}

但是,它只会为使用OnPush 策略的顶级组件调用。它不会在子组件上调用:

     normal component
           |
  child OnPush component  <---- ngDoCheck called only for this component
           | 
    child component 1     <---- ngDoCheck is not called for this component
           |
    child component 2     <---- ngDoCheck is not called for this component

为什么会触发?

触发它是为了让您有机会在此挂钩中执行您自己的自定义逻辑,并选择运行一次更改检测周期,即使 @Inputs 没有更改:

class OnPushComponent {
   constructor(private cd: ChangeDetectorRef) {}

   ngDoCheck() {
      if (some check) {
          this.cd.markForCheck();
      }
   }
}

另请参阅Angular ngDoCheck() gets called even with ChangeDetectionStrategy.OnPush 这个答案以获取真实用例示例。

【讨论】:

  • 谢谢,您的帖子应该是官方 Angular 文档的一部分。我不敢相信他们没有涵盖这个困难的话题......所以当一个事件在子 1 组件上引发时,它会开始一个从最顶层组件到其子组件的更改检测周期,对吗?因此,如果我想阻止子 2 的更改检测,我必须在父组件上阻止它上层的杠杆,并在其上设置 OnPush 属性
  • ngDoCheck 并不意味着正在为您的组件运行更改检测,这意味着正在检查父组件。在您的设置中,您不需要做任何特别的事情。 child2 组件不会运行更改检测。您可以通过添加child2a 作为child2ё and see that ngDoCheck 的子项来测试它,当您修改child1 时不会触发它。阅读我提到的文章
  • 我不明白,在您的回复中,您这样说:“子组件 1
  • 原来的 plunker 已经显示了问题,每次我只需单击输入文本 ngDoCheck 都会为所有孩子调用,这里是一个更新的 plunker,只有 2 个以上的孩子:plnkr.co/edit/IABey8nkuUoV9H0ZA6DY?p=preview 我是现在注意到只有在开发人员控制台打开时从一个输入切换到另一个输入(或输入某些内容)真的很慢......无论如何,即使我的根组件有 OnPush,ngDoCheck 函数也在调用
  • 对于我看到的 plunker 非常慢,因为所有这些 ngDoCheck 调用,所以我认为我必须找到一种方法来跳过它们。使用 OnPush 或分离任何东西
猜你喜欢
  • 2018-05-14
  • 2017-04-17
  • 2019-07-09
  • 2018-01-17
  • 2019-02-15
  • 2020-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多