【问题标题】:Angular markForCheck vs detectChanges角度 markForCheck 与 detectChanges
【发布时间】:2019-12-14 06:44:46
【问题描述】:

我将从 我在 StackOverflow 上看到类似问题的概念开始这个问题,但这个问题只回答了差异

我要问的是我应该根据情况使用什么以及一种或另一种方法可能有什么缺点

我知道detectChanges 对元素及其子元素运行即时更改检测周期,同时markForCheck 仅将当前元素及其祖先标记为脏,并且应在下一个更改检测周期检查它们。

我问这个主要是因为我觉得我不应该总是在异步调用中使用markForCheck

例如,我有一个InputComponent,它是常规 HTML 输入的包装器。这个InputComponent 启用了ChangeDetectionStrategy.OnPush

当我对服务器进行异步调用并获取数据时,我需要在 InputComponent 上运行更改检测以更新选项列表,我有两个选项。

首先(我觉得我应该使用的)是detectChanges,因为它只会对这个确切的组件应用检查,而markForCheck 会导致检查整个树枝。

那么我应该使用什么,我需要使用markForCheck 吗?为什么?

【问题讨论】:

    标签: angular typescript performance angular-changedetection


    【解决方案1】:

    我要问的是我应该根据情况使用什么以及一种或另一种方法可能有什么缺点。

    您应该永远不要致电detectChanges()

    detectChanges() 为开发人员提供价值的情况并不理想。它通常用在程序员没有很好地管理组件的不变性、状态管理和变异的项目中。

    所有需要detectChanges()的源代码都可以重写,使其不再需要。

    另一方面,markForCheck() 确实有很好的边缘情况,应该使用它。

    我问这个主要是因为我觉得我不应该总是在异步调用中使用 markForCheck。

    您经常会在调用markForCheck() 的源代码附近找到对this 的引用。

    @Component({...})
    export class ExampleComponent {
        //......
        public function work() {
            this.httpClient.get(...).subscribe(resp => 
                this.data = resp.data;
                this.changeDetectorRef.markForCheck();
            });
        }
    }
    

    在函数式编程中,对this 的引用是不纯的,它会改变函数范围之外的外部状态。打破函数式编程最佳实践会引入需要修复以保持一切正常的问题。如果您只使用异步操作编写纯函数,则无需调用 markForCheck(),但一旦引入 this 引用,组件状态就会发生变化,并且需要通知视图。

    上面没有错,但同时在 RxJS 订阅中过度使用this 会产生难以维护的源代码。

    最好重写源代码以使用响应式编程,并在模板中使用async 管道。关键是创建无状态的组件,这样组件上的属性就不需要更新了。一切都是作为反应流完成的。

    @Component({
        template: `<ng-container *ngIf="data$ | async as data">
                   <!-- stuff -->
                   </ng-container>`,
        // .....
    })
    export class ExampleComponent {
        public data$: Observable<any>;
    
        public function work() {
            this.data$ = this.httpClient.get(...).pipe(shareReplay(1));
        }
    }
    

    如果您将组件设计为无状态,并使用 RxJS 进行所有数据处理,则不需要使用 markForCheck()。即使您侦听 DOM 事件,也可以将数据通过管道传输到其他可观察对象以避免使用 this

    虽然有时您必须致电markForCheck()。我建议您停止并重新考虑您的方法以避免使用它,因为应该有另一种不需要它的方法。

    【讨论】:

    • 但这不是对性能的巨大提升吗,因为markForCheck 会导致更新整个分支而不是单个组件及其后代?此外,您还谈到了不变性。为什么我们要谈论不变性和markForCheck/detectChanges 方法?据我所知,第一个“计划”更改检测,而第二个立即执行。无论如何,两者都以不同的方式和“体积”完成相同的工作
    • @Sergey 易于阅读和维护的方法可能是最适合您的方法。尝试使用 RxJS 做更多事情,并在模板中使用更多 thing$ | async 来学习。需要时间来改变你的想法,这样你就可以使用RXJS解决问题,但是以后你会发现它的更多用法,需要少用markForCheck()。这就是我能说的。您可以通过提出问题来了解您正在开始学习。
    • 我一直在问自己和@Sergey 一样的问题。而@Reactgular 的回应实际上并没有回答这个问题。 async 只是将markForCheck 隐藏在里面。但是从根到我的组件的整个分支都被标记为脏并重新评估。如果此分支中的任何组件在模板中包含方法,则将调用此方法。另一方面,detectChanges() 总是向下 - 所以如果该组件的直接子级使用 OnPush,那么 detectChanges() 的唯一效果将是检查单个组件。这似乎正是开发人员想要实现的目标。
    • @amakhrov 现在我知道这里的关键区别在于 detectChanges 实际上 运行 更改检测同时 markForCheck 只是 到 Angular 应该检查它。对我们而言,这意味着三重 markForCheck 将导致一次更改检测,而三重 detectChanges 将导致三重更改检测运行,这可能导致性能大幅下降
    • @Sergey - 同意多次运行 detectChange() 的观点。不过,实现基于 Promise 的 debounce 非常容易。无论如何,我几乎想不出一个真实的案例,我想连续多次调用它。 @Reactgular“如果您使用它来解决异步副作用,那么您可能应该使用异步管道来实现您的代码”。假设我正在实现自己的异步管道。为什么我要在内部使用markForCheck()(这就是异步管道的作用)而不是detectChanges()
    猜你喜欢
    • 2017-05-12
    • 2019-01-06
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    • 2017-10-23
    • 1970-01-01
    • 2022-12-25
    相关资源
    最近更新 更多