在与ChangeDetectionStrategy.OnPush个观察值一起使用时,我正试图理解最佳实践.

该示例演示了希望显示某种加载消息(或简单的微调器动画)的常见场景:

Plnkr here

@Component({
  selector: 'my-app',
  template: `Are we loading?: {{loadingMessage}}`,

  // Obviously "Default" will notice the change in `loadingMessage`...
  // changeDetection: ChangeDetectionStrategy.Default

  // But what is best practice when using OnPush?
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class App implements OnInit {

  private loadingMessage = "Wait for it...";

  constructor() {

  }

  ngOnInit() {

    // Pretend we're loading data from a service.
    // This component is only interested in the call's success
    Observable.of(true)
      .delay(2000)
      .subscribe(success => {

        if(success){
          console.log('Pretend loading: success!');

          // OnPush won't detect this change
          this.loadingMessage = 'Success!';
        }

      });

  }
}

我或多或少理解OnPush的不变性要求,至少对我来说,目前在谈论实际的模型数据(可能保存在某种store 中)时这是有意义的.

所以,我有两个问题:

  1. 为什么新字符串值'Success!'的赋值不会触发更改检测器?就不变性而言,这个值已经改变了,对吗?
  2. 当使用ChangeDetectionStrategy.OnPush时,应该如何实现轻量级内部组件状态(即loadingMessage)?如果有多种最佳实践,请为我指出正确的方向.

推荐答案

问得好.我引用了Savkin关于onPush的两句话(因为Angular.io文档似乎还没有关于这个话题的任何信息):

只有当组件的输入发生变化或组件的模板发出事件时,框架才会判断OnPush个组件.-ref

使用OnPush时,Angular只会在组件的任何输入属性发生变化、触发事件或可观测事件时判断组件.-ref(在回复@vivainio的 comments 中)

第二句话似乎更完整.(可惜被一条 comments 淹没了!)

为什么新字符串值Success!的赋值不触发

OnPush不可变性引用的是输入属性,而不是普通实例属性.如果loadingMessage是输入属性,并且值已更改,则将在组件上执行更改检测.(参见@Vlado对Punker的回答.)

当使用ChangeDetectionStrategy.OnPush时,应该如何实现轻量级内部组件状态(即loadingMessage)?如果有多种最佳实践,请为我指出正确的方向.

以下是我目前所知道的:

  • 如果我们作为处理事件的一部分或可观察到的触发的一部分更改内部状态,则在OnPush组件上执行更改检测(即,视图将更新).在你的特殊情况下,我很惊讶视图没有更新.以下是两种猜测:
  • 如果我们改变内部状态并且它不是事件处理或可观察激发的一部分,我们可以将ChangeDetectorRef注入我们的组件并调用方法markForCheck()以使改变检测在OnPush组件和直到根组件的所有祖先组件上执行.如果仅视图状态被改变(即,对于组件以及可能对其后代是本地的状态),则可以改为使用detectChanges(),这将不会将所有祖先组件标记为用于改变检测.Plunker

Angular相关问答推荐

垫-菜单未以17.2.3角显示

带有服务依赖的Angular测试类

Angular中的Bootstrap carousel动态属性

对子router-outlet 应用主机样式

将sass与@Use语句一起使用时出现Angular 新的VITE构建器编译错误

从API动态加载MAT表数据时禁用分页

Angular 迁移 14 到 15 / 16:angular universal 是否停止将 <!-- this page was prerendered with angular universal --> 用于预渲染页面?

Angular 15 Ref 错误:初始化前无法访问组件 A

清除Angular2中的输入文本字段

Angular/RxJS 6:如何防止重复的 HTTP 请求?

在 Ionic 2 中使用图像资源的正确方法

如何在 Angular4 中访问组件的 nativeElement?

等待多个promises完成

Angular 2 单元测试 - @ViewChild 未定义

将 Angular 5 升级到 6 时,我得到不兼容的对等依赖(使用 ng update @angular/core)

如何每隔一段时间发出 HTTP 请求?

Angular2material对话框自动关闭

formGroup 需要一个 FormGroup 实例

如何作为模块的子模块路由到模块 - Angular 2 RC 5

如何在 Angular 2 中跟踪路由?