Angular 性能:如果组件超出视口,则更改检测分离
Angular performance: change detection detached if component is out of viewport
我想从当前视口中分离所有组件的变化检测
import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, ElementRef, ViewChild, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
@Component({
selector: 'hello',
template: `<div #counter>[{{index}}] {{count}}</div>`,
styles: [`div { border: 1px solid red; padding: 5px 5px 5px 5px; }`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelloComponent implements OnInit, AfterViewInit {
@ViewChild('counter', { static: false }) counter: ElementRef;
@Input() index: number;
public count = 0;
public visible = true;
constructor(private cdr: ChangeDetectorRef){}
ngOnInit() {
setInterval(() => {
this.count++;
this.cdr.markForCheck();
}, 1000);
}
ngAfterViewInit() {
const hideWhenBoxInView = new IntersectionObserver((entries) => {
if (entries[0].intersectionRatio <= 0) { // If not in view
this.cdr.detach();
this.visible = false;
} else {
this.visible = true;
this.cdr.reattach();
this.cdr.markForCheck();
}
// console.log(this.index, this.visible);
});
hideWhenBoxInView.observe(this.counter.nativeElement);
}
}
它可以工作,但是超过 1000 个组件时性能很差。
我的 attaching/detaching 变更检测是否正确?
您正在为每个组件调用 setInterval()
,包括那些不在视图中的组件。更改检测不是 运行 但您仍在每秒调用 setInterval()
中的函数 1000 次,这解释了延迟。
顺便说一下,渲染包含 1000 个项目的滚动列表也会影响性能。尽管不在视口中,但浏览器将渲染所有内容,并且在滚动列表时需要计算各种绘制。您应该懒惰地呈现这么长的列表,请参阅 Virtual Scrolling in Angular 7
您还在对不在视图中的组件调用 .markForCheck()
,请在调用之前检查组件是否可见。
ngOnInit() {
this.subscriptions.add(
interval(1000).subscribe(() => {
this.count++;
if (this.visible) {
this.cdr.markForCheck();
}
})
);
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
ngAfterViewInit() {
const hideWhenBoxInView = new IntersectionObserver(entries => {
if (entries[0].intersectionRatio <= 0) {
// If not in view
this.cdr.detach();
this.visible = false;
} else {
this.visible = true;
this.cdr.reattach();
this.cdr.markForCheck();
}
});
hideWhenBoxInView.observe(this.counter.nativeElement);
}
也许使用 trackBy
可以避免检查它是否在视口中。
<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>
trackBy
让您可以选择 property/condition angular 应该检查更改的对象。
trackByFn(index, item) {
return item.someUniqueIdentifier;
// return index(if id is not unique) or unique id;
}
A function that defines how to track changes for items in the
iterable.
When items are added, moved, or removed in the iterable, the directive
must re-render the appropriate DOM nodes. To minimize churn in the
DOM, only nodes that have changed are re-rendered.
By default, the change detector assumes that the object instance
identifies the node in the iterable. When this function is supplied,
the directive uses the result of calling this function to identify the
item node, rather than the identity of the object itself.
The function receives two inputs, the iteration index and the node
object ID.
我想从当前视口中分离所有组件的变化检测
import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, ElementRef, ViewChild, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
@Component({
selector: 'hello',
template: `<div #counter>[{{index}}] {{count}}</div>`,
styles: [`div { border: 1px solid red; padding: 5px 5px 5px 5px; }`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelloComponent implements OnInit, AfterViewInit {
@ViewChild('counter', { static: false }) counter: ElementRef;
@Input() index: number;
public count = 0;
public visible = true;
constructor(private cdr: ChangeDetectorRef){}
ngOnInit() {
setInterval(() => {
this.count++;
this.cdr.markForCheck();
}, 1000);
}
ngAfterViewInit() {
const hideWhenBoxInView = new IntersectionObserver((entries) => {
if (entries[0].intersectionRatio <= 0) { // If not in view
this.cdr.detach();
this.visible = false;
} else {
this.visible = true;
this.cdr.reattach();
this.cdr.markForCheck();
}
// console.log(this.index, this.visible);
});
hideWhenBoxInView.observe(this.counter.nativeElement);
}
}
它可以工作,但是超过 1000 个组件时性能很差。
我的 attaching/detaching 变更检测是否正确?
您正在为每个组件调用 setInterval()
,包括那些不在视图中的组件。更改检测不是 运行 但您仍在每秒调用 setInterval()
中的函数 1000 次,这解释了延迟。
顺便说一下,渲染包含 1000 个项目的滚动列表也会影响性能。尽管不在视口中,但浏览器将渲染所有内容,并且在滚动列表时需要计算各种绘制。您应该懒惰地呈现这么长的列表,请参阅 Virtual Scrolling in Angular 7
您还在对不在视图中的组件调用 .markForCheck()
,请在调用之前检查组件是否可见。
ngOnInit() {
this.subscriptions.add(
interval(1000).subscribe(() => {
this.count++;
if (this.visible) {
this.cdr.markForCheck();
}
})
);
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
ngAfterViewInit() {
const hideWhenBoxInView = new IntersectionObserver(entries => {
if (entries[0].intersectionRatio <= 0) {
// If not in view
this.cdr.detach();
this.visible = false;
} else {
this.visible = true;
this.cdr.reattach();
this.cdr.markForCheck();
}
});
hideWhenBoxInView.observe(this.counter.nativeElement);
}
也许使用 trackBy
可以避免检查它是否在视口中。
<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>
trackBy
让您可以选择 property/condition angular 应该检查更改的对象。
trackByFn(index, item) {
return item.someUniqueIdentifier;
// return index(if id is not unique) or unique id;
}
A function that defines how to track changes for items in the iterable.
When items are added, moved, or removed in the iterable, the directive must re-render the appropriate DOM nodes. To minimize churn in the DOM, only nodes that have changed are re-rendered.
By default, the change detector assumes that the object instance identifies the node in the iterable. When this function is supplied, the directive uses the result of calling this function to identify the item node, rather than the identity of the object itself.
The function receives two inputs, the iteration index and the node object ID.