带有子组件发出事件的Angular ChangeDetectionStrategy.OnPush [英] Angular ChangeDetectionStrategy.OnPush with child component emitting an event

查看:172
本文介绍了带有子组件发出事件的Angular ChangeDetectionStrategy.OnPush的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白为什么在这种情况下运行子组件的更改检测:

I don't understand why the child component's change detection runs in this scenario:

import { Component, ChangeDetectionStrategy } from '@angular/core'

@Component({
  selector: 'app-root',
  template: `
    <cmp [ticks]="ticks" (update)="onUpdate($event)"></cmp>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {

  ticks = 0;

  onUpdate(event) {
    console.log(this.ticks);
  }
}


import { Component, ChangeDetectionStrategy, OnInit, Input, Output, EventEmitter, OnChanges } from '@angular/core';

@Component({
  selector: 'cmp',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<p>Number of ticks: {{ticks}}</p>
`
})
export class CmpComponent implements OnInit, OnChanges {
  @Input('ticks') ticks: number;
  @Output() update: EventEmitter<number> = new EventEmitter();

  ngOnInit() {
    setInterval(() => {
      this.ticks++;
      this.update.emit(this.ticks);
    }, 1000);
  }

  ngOnChanges() {
    console.log('changed');
  }
}

当我运行该滴答数"时,会在子视图中对其进行更新.

When I run this 'Number of ticks' is updated in the child view.

当我取消在父级中监听事件时,它不会更新视图.

When I remove listening to the event in the parent it does not update the view.

我的理解是:

由于父级实现了OnPush策略,因此在侦听从子级发出的事件时会触发更改检测.接收到该事件后,它不会更改'tick',因此子组件的@Input()不会更新.但是也实现OnPush策略的子组件会更新其视图.因此,它的行为就像其@Input已更改.

Because the parent implements the OnPush strategy it triggers change detection when it listens to the event that is emitted from the child. On receiving the event it does not alter 'tick' and therefore the child component's @Input() is not updated. Yet the child component which also implements OnPush strategy updates its view. It therefore acts as if its @Input changed.

根据我的研究:

使用OnPush策略时,如果发生以下情况,将对组件进行更改检测:

With the OnPush strategy the change detection happens for a component if:

  • 在组件本身上收到绑定事件.
  • @Input()已更新
  • |异步管道收到事件
  • 更改检测是手动"调用的

这些似乎都不适用于子组件.

None of these seem to be applicable to the child component.

有什么解释吗?非常感激.

Any explanations? Much appreciated.

推荐答案

首先,感谢您提出了一个很好的问题.

First of all thanks for a good question.

使用angular 2.x.x,它将按您期望的方式工作.

With angular 2.x.x it will work the way as you expect.

https://plnkr.co/edit/TiOeci5Lr49xvRB5ozHb?p=preview

但是在angular4中引入了新的View Engine,并且所有代码都被完全覆盖.

But in angular4 was introduced new View Engine and all code was completelly overwritten.

https://plnkr.co/edit/SFruiPXEhMmYDP7WuBbj?p=preview

事件发生时,角度调用一些称为 markForCheck 的方法.

When event happens angular calls some method known as markForCheck.

角度2版本

AppView.prototype.markPathToRootAsCheckOnce = function () {
  var /** @type {?} */ c = this;
  while (isPresent(c) && c.cdMode !== ChangeDetectorStatus.Detached) {
    if (c.cdMode === ChangeDetectorStatus.Checked) {
      c.cdMode = ChangeDetectorStatus.CheckOnce;
    }
    if (c.type === ViewType.COMPONENT) {
      c = c.parentView;
    }
    else {
      c = c.viewContainer ? c.viewContainer.parentView : null;
    }
  }
};

角度4版本

function markParentViewsForCheck(view) {
  var /** @type {?} */ currView = view;
  while (currView) {
    if (currView.def.flags & 2 /* OnPush */) {
      currView.state |= 8 /* ChecksEnabled */;
    }
    currView = currView.viewContainerParent || currView.parent;
  }
}

尽管代码看起来完全不同,但这里没有区别.它从当前组件开始,并允许检查所有父组件,直到根组件为止.

Despite the fact that the code looks completely different there is no difference here. It starts with current component and enables check for all parent components up to root component.

我突出了短语"从当前组件开始",因为这确实有所改变.

I highlighted the phrase starts with current component because this is exactly what has changed.

Angular 2.x.x以 AppComponent

Angular 2.x.x starts with AppComponent

View_App0.prototype.handleEvent_4 = function(eventName,$event) {
  var self = this;
  self.debug(4,2,3);
  self.markPathToRootAsCheckOnce(); // self is AppComponent view
  var result = true;
  if ((eventName == 'update')) {
    var pd_sub_0 = (self.context.onUpdate($event) !== false);
    result = (pd_sub_0 && result);
  }
  return result;
};

Angular 4以 CmpComponent

Angular 4 starts with CmpComponent

function dispatchEvent(view, nodeIndex, eventName, event) {
    var nodeDef = view.def.nodes[nodeIndex];
    var startView = nodeDef.flags & 33554432 /* ComponentView */ ? asElementData(view, nodeIndex).componentView : view;
    markParentViewsForCheck(startView);
    return Services.handleEvent(view, nodeIndex, eventName, event);
}

因此,将打开 CmpComponent 进行检查

这篇关于带有子组件发出事件的Angular ChangeDetectionStrategy.OnPush的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆