我有一个角度组件问题,该组件连接到第三方 API PayPal BrainTree,该 API 动态地将 iframe 添加到组件主体。我需要保持连接和底层 html 持久化,而不是断开与 api 的连接。
有问题的组件与多个支付选项相关,其中一个选项连接到第三方 api。当另一个请求被触发以刷新支付方式时,问题就会出现,当这种情况发生时,支付选项会重新呈现,这会在建立新连接和创建新 iframe 之前断开 API 并从组件中删除 iframe。>
Here is a working Minimum viable example for reference.
渲染的基础是
<div *ngFor="let option in payments$ | async">
<app-lazy-payment-option *ngIf="option.lazy"></app-lazy-payment-option>
<app-payment-option *ngIf="!option.lazy"></app-payment-option>
</div>
在 LazyPaymentOption 中,与第三方支付 API 的连接是在 ngAfterViewInit 期间进行的
@Component(...)
class LazyPaymentOptionComponent implements AfterViewInit {
ngAfterViewInit(): void {
this.thirdPartyService.connect().subscribe();
}
}
理想情况下,我需要防止 LazyPaymentOption 破坏和重新渲染,在 React 中我会使用 shouldComponentUpdate 但我在组件级别的 Angular 中没有找到类似的东西。
我找到了 ChangeDetectorRef,但我似乎无法阻止组件破坏和重新渲染,例如。我希望我可以使用 this.cd.detatch()
来防止卸载和重新渲染组件。
@Component(...)
class LazyPaymentOptionComponent implements AfterViewInit {
constructor(
private cd: ChangeDetectorRef,
private thirdPartyService: ThirdPartyService
) {}
ngAfterViewInit(): void {
this.thirdPartyService.connect().subscribe(() => {
// attempt to detach from change detection so the component doesn't re-render
this.cd.detatch();
});
}
}
在我手动进行渲染之前,我可以做些什么来阻止渲染周期?或者我应该尝试的其他任何事情,我已经深入研究了这个问题 2 天,但不确定我可以在哪里查看。
答案 0 :(得分:1)
您可以为此使用变更检测策略。
@Component({
selector: '...',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `...`
})
export class ...Component {
默认的变更检测策略实际上会重新渲染很多组件。几乎每当发生可能改变您的一个 getter 值的事情时,它就会重新呈现。
相比之下,OnPush
策略仅在
| async
管道具有新值由于这种行为,它也不会自动检测某些更改。 (例如,getter 的值已更改。)在这些情况下,您可能必须强制重新渲染它。
constructor(private cdr: ChangeDetectorRef) {}
onInit() {
setInterval( () => {
// this is something an OnPush strategy won't detect.
this.counter++;
// so you have to mark it for a rerender.
this.cdr.markForCheck();
}, 1000);
}
总的来说,出于性能原因,我尽量让我的大部分组件与 OnPush
策略一起使用。
根据经验,您最终可能会编写很多 markForCheck
。听起来事实并非如此。您应该更积极地使用异步管道。例如在前面的示例中,您可以将计数器建模为 BehaviorSubject
。
public counter$ = new BehaviorSubject();
// note: in template file, use "counter$ | async"
onInit() {
let counter = 0;
setInterval( () => {
// this is something an OnPush strategy won't detect.
counter++;
this.counter$.next(counter);
}, 1000);
}
从另一边看。每个 setTimeout
实际上都可以重新渲染大量组件(具有默认更改检测策略的组件)。如果您事先知道它不需要重新渲染任何组件,您实际上也可以阻止它:
constructor(private ngZone: NgZone)
onInit() {
// make the contained changes undetectable for angular.
ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.counter++;
}, 1000);
});
}