消除Angular ngDoCheck生命周期挂钩

Debounce Angular ngDoCheck lifecycle hook

我一直在进行一些有关在Angular组件中处理状态的实验。

假设我有一个计算成本很高的变量,它取决于其他几个变量。 我可以编写一个函数updateVar(),并在我知道可能会影响它的每个事件上调用它。 通过订阅。 但是,在5个以上的不同位置调用上述函数时,它不会感到很干燥,也不会很健壮。

因此,反跳DoCheck Angular生命周期挂钩怎么样呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
docheckSubject: Subject = new Subject();
debounced: boolean = false;

constructor(private cd: ChangeDetectorRef) {}

ngOnInit() {
  this.docheckSubject.debounceTime(50).subscribe(
    () => this.ngDoCheckDebounced()
  );
}

ngDoCheck() {
  if (!this.debounced) {
    this.docheckSubject.next();
  } else {
    this.debounced = false;
  }
}

ngDoCheckDebounced() {
  this.debounced = true;
  this.updateVar();
  this.cd.detectChanges();  // needed to reflect update in DOM
}

这是一个小例子。

这种方法在我的真实应用中似乎可以正常工作,当然,更多的ngDoCheck()正在发生,并且量角器测试也不会抱怨。 但是我无法摆脱做一个肮脏,不太聪明的黑客的感觉。

问:这会咬我吗? 实际上,我是否需要它?


经过几个月没有问题的生产(AFAICT),我最终删除了它,因为它最终导致e2e(量角器)测试受阻,直到有人手动单击页面。显然是不可持续的。

我无法正确诊断此问题的原因,它可能是源于我们代码中无关部分的怪异现象,但是比后悔更安全。


ngDoCheck经常被调用:

https://angular.io/guide/lifecycle-hooks#docheck

This hook is called with enormous frequency—after every change detection cycle no matter where the change occurred.

将此钩子用于除Angular不知道的变量以外的其他任何东西以寻找变更检测是一个坏主意。 (例如,输入对象更改其属性之一的值)。防弹跳有所帮助,但是您仍然非常频繁地执行该逻辑,并且在不再需要该逻辑时(例如,当debounced == false时)将继续执行该操作。

我认为您的解决方案可以使用,但是我认为这种方法的开销比其他方法差得多。可能值得将更新逻辑作为Observable链的一部分,或者将您的Observables传递到将这种逻辑添加到链中的函数中。


您能否将变量放入getter中,并且仅在检索到变量时重新计算?

这是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//our root app component
import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `
   
      Hello {{name}}
      x={{x}}
      y={{y}}
      <button (click)="increment()">Increment</button>
      Calculated Value={{calculatedValue}}
   
  `,
})
export class App {
  name:string;
  x: number = 0;
  y: number = 10;

  get calculatedValue(): number {
    return this.x * this.y;
  }

  constructor() {
    this.name = `Angular! v${VERSION.full}`
  }

  increment(): number {
    this.x++;
  }

}

以及相关的Plunker:https://plnkr.co/edit/QC7mkbsbjRRBjJOjWSHe?p=preview