开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
变更检测的性能
默认情况下,当我们的组件中某个值发生了变化触发了变更检测,那么Angular会从根组件上往下检查所有的组件。不过Angular对每个组件进行变更检测的速度非常快。 尽管 Angular 进行了大量优化,但是遇到了大型应用,变化检测的性能仍然会下降,所以为了提升性能,就要减少变更检测的次数。
减少变更检测次数
我们可以使用 OnPush的策略来优化我们的应用,那么这就够了吗? 在我们实际的开发中还会有很多的场景,我们需要通过一些其他的方式来继续优化我们的应用。
场景一
@Component({
selector: "app-enter",
template: `<input #input type="text" />`,
})
export class EnterComponent implements AfterViewInit {
@ViewChild("input", { read: ElementRef })
private inputElementRef: any;
constructor() {}
ngAfterViewInit(): void {
this.inputElementRef.nativeElement.addEventListener(
"keydown",
(event: KeyboardEvent) => {
const keyCode = event.which || event.keyCode;
if (keyCode === 13) {
this.search();
}
}
);
}
search() {
// ...
}
}
事件会触发Angular的变更检测,在示例中绑定 keydown 事件后,每一次键盘输入都会触发变更检测,而这些变更检测大多数都是多余的检测,只有当按键为 Enter 时,才需要真正的进行变更检测。
上面的例子,如果不想让事件触发变更检测,怎么实现呢?我们就可以利用 NgZone.runOutsideAngular() 来减少变更检测的次数。
基于上面的例子,我们用指令封装一下。
@Directive({
selector: '[enter]'
})
export class ThyEnterDirective implements OnInit {
@Output() enter = new EventEmitter();
constructor(private ngZone: NgZone, private elementRef: ElementRef<HTMLElement>) {}
ngOnInit(): void {
// 包裹代码将运行在Zone区域之外
this.ngZone.runOutsideAngular(() => {
this.elementRef.nativeElement.addEventListener('keydown', (event: KeyboardEvent) => {
const keyCode = event.which || event.keyCode;
if (keyCode === 13) {
this.ngZone.run(() => {
this.enter.emit(event);
});
}
});
});
}
}
场景二
假如我们使用 WebSocket 将大量数据从后端推送到前端,则相应的前端组件应仅每 10 秒更新一次。在这种情况下,我们可以通过调用detach() 和手动触发它来停用变更检测。 这里暂时模拟一下,首先要detach()一下,变更检测就跳过了,然后在需要执行变更检测的地方手动调用变更检测就可以了。
@Component({
selector: 'main',
template: `
<h1>{{count}}</h1>
`
})
export class MainComponent {
@Input() actionSubject$!: Subject<any>;
@Input() options: any;
count: number = 0;
constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() {}
cdr.detach(); // 停用变化检测
setInterval(() => {
this.cdr.detectChanges(); // 手动触发变化检测
}, 10 * 1000);
}
问题: 当页面完成加载后,页面上显示什么?
当然使用 ****ngZone.runOutsideAngular() 也可以处理这种场景。
实际上,上面的两个例子是相通的。
总结
为了提升性能,我们就需要尽量的去减少变更检测的次数,除了之前说的可以使用OnPush策略来减少变更检测次数,还可以采用本文中的一些方法。