Angular 4 分离 children 之间的变化检测

Angular 4 detach change detection between children

我不明白为什么即使我使用 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个相同的children:

@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 函数。

想想有几千个 children,它真的很慢...

谢谢!

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

  1. calls OnInit and ngDoCheck on a child component (OnInit is called only during first check)
  2. runs change detection for a child view (repeats the steps in this list)

正如您所见,ngDoCheck 始终在子组件上触发。当尝试 运行 更改子组件的检测时, 之后执行 OnPush 检查。这是引用:

Finally, change detection for the current view is responsible for starting change detection for child views (operation 8). This is the place where state of the child component view is checked and if it’s ChecksEnabled, then for this view the change detection is performed. Here is the relevant code:

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();
      }
   }
}

另请参阅 此答案以获取真实用例示例。