zoukankan      html  css  js  c++  java
  • angular 2+ 变化检测系列二(检测策略)

      我们将创建一个简单的MovieApp来显示有关一部电影的信息。这个应用程序将只包含两个组件:显示有关电影的信息的MovieComponent和包含执行某些操作按钮的电影引用的AppComponent。 我们的AppComponent将有三个属性:slogan,title和actor。最后两个属性将传递给模板中引用的MovieComponent元素。

    // app/app.component.ts
    import {Component} from '@angular/core'; import {MovieComponent} from './movie.component'; import {Actor} from './actor.model'; @Component({ selector: 'app-root', template: ` <h1>MovieApp</h1> <p>{{ slogan }}</p> <button type="button" (click)="changeActorProperties()"> Change Actor Properties </button> <button type="button" (click)="changeActorObject()"> Change Actor Object </button> <app-movie [title]="title" [actor]="actor"></app-movie>` }) export class AppComponent { slogan = 'Just movie information'; title = 'Terminator 1'; actor = new Actor('Arnold', 'Schwarzenegger'); changeActorProperties() { this.actor.firstName = 'Nicholas'; this.actor.lastName = 'Cage'; } changeActorObject() { this.actor = new Actor('Bruce', 'Willis'); } }

      在上面的代码片段中,我们可以看到我们的组件定义了两个触发不同方法的按钮。 changeActorProperties将通过直接更改actor对象的属性来更新影片的主角。相反,方法changeActorObject将通过创建一个全新的Actor类实例来更改actor的信息。Actor模型非常简单,它只是一个定义actor的firstName和lastName的类。

    // app/actor.model.ts
    export class Actor { constructor( public firstName: string, public lastName: string) {} }

    最后,MovieComponent显示AppComponent在其模板中提供的信息

    // app/movie.component.ts
    import { Component, Input } from '@angular/core'; import { Actor } from './actor.model'; @Component({ selector: 'app-movie', template: ` <div> <h3>{{ title }}</h3> <p> <label>Actor:</label> <span>{{actor.firstName}} {{actor.lastName}}</span> </p> </div>` }) export class MovieComponent { @Input() title: string; @Input() actor: Actor; }

    Change Detector类

      在应用程序运行时,Angular将创建称为更改检测器的特殊类,每个组件对应于我们定义的每个组件。在这种情况下,Angular将创建两个类:AppComponent和AppComponent_ChangeDetector.变更检测器的目标是知道自上次更改检测过程运行以来,组件模板中使用的模型属性已更改.为了解这一点,Angular创建了一个适当的变更检测器类的实例,以及一个它应该检查的组件的链接。

      在我们的示例中,因为我们只有AppComponent和MovieComponent的一个实例,所以我们只有一个AppComponent_ChangeDetector实例和MovieComponent_ChangeDetector实例。下面的代码片段是App​​Component_ChangeDetector类的外观概念模型。

    class AppComponent_ChangeDetector {
    
      constructor(
        public previousSlogan: string,
        public previousTitle: string,
        public previousActor: Actor,
        public movieComponent: MovieComponent
      ) {}
    
      detectChanges(slogan: string, title: string, actor: Actor) {
        if (slogan !== this.previousSlogan) {
          this.previousSlogan = slogan;
          this.movieComponent.slogan = slogan;
        }
        if (title !== this.previousTitle) {
          this.previousTitle = title;
          this.movieComponent.title = title;
        }
        if (actor !== this.previousActor) {
          this.previousActor = actor;
          this.movieComponent.actor = actor;
        }
      }
    }
    

      因为在我们AppComponent的模板中我们引用了三个变量(slogan,title和actor),我们的变换检测器将有三个属性来存储这三个属性的“旧”值,以及对它应该”的AppComponent实例的引用。当更改检测过程想要知道我们的AppComponent实例是否已更改时,它将运行方法detectChanges传递当前模型值以与旧模型值进行比较。如果检测到更改,则组件会更新。

      译者注:这只是变更检测器类如何工作的概念性概述;实际的实施可能会有所不同。

    变化检测策略一: Default

      默认情况下,Angular为应用程序中的每个组件定义了一个特定的更改检测策略。为了使这个定义明确,我们可以使用@Component装饰器的属性changeDetection。如下代码

    // app/movie.component.ts
    import { ChangeDetectionStrategy } from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.Default // 如果不写,缺省值为Default }) export class MovieComponent { // ... }

      让我们看看当用户在使用Defalut策略时单击“更改Actor属性”按钮时会发生什么。更改由事件触发,更改的传播分两个阶段完成:应用阶段和变更检测阶段.

    1. 阶段一(应用阶段) 在第一阶段,应用程序(我们的代码)负责更新模型以响应某些事件。在此方案中,属性actor.firstName和actor.lastName已更新。
    2. 阶段一(变更检测阶段) 现在我们的模型已更新,Angular必须使用更改检测更新模板。变更检测始终从根组件开始,在本例中为AppComponent,并检查绑定到其模板的任何模型属性是否已更改,将每个属性的旧值(在事件触发之前)与新属性(之后)进行比较模型已更新)。 AppComponent模板引用了三个属性,slogan,title和actor,因此其相应的变化检测器进行的比较如下所示:
    Is slogan !== previousSlogan? No, it's the same.
    Is title !== previousTitle? No, it's the same.
    Is actor !== previousActor? No, it's the same.
    

      请注意,即使我们更改了actor对象的属性,我们也始终使用相同的实例.因为我们正在进行浅层比较,所以即使其内部属性值确实发生变化,询问actor!== previousActor的结果也总是为false。即使更改检测器无法找到任何更改,更改检测的默认策略是遍历树的所有组件,即使它们似乎没有被修改(我们可以手动优化).

      接下来,更改检测在组件层次结构中向下移动,并检查绑定到MovieComponent模板的属性,执行类似的比较:

    Is title !== previousTitle? No, it's the same.
    Is actorFirstName !== previousActorFirstName? Yes, it has changed.
    Is actorLastName !== previousActorLastName? Yes, it has changed.
    

      最后,Angular检测到绑定到模板的某些属性已更改,因此它将更新DOM以使视图与模型同步.  

    变化检测策略二: Onpush

      为了通知Angular我们将遵守之前提到的条件以提高性能,我们将在MovieComponent上使用OnPush更改检测策略.

    // app/movie.component.ts
    @Component({
    // ... changeDetection: ChangeDetectionStrategy.OnPush // 显示调用Onpush策略 }) export class MovieComponent { // ... }

      这将通知Angular:我们的组件仅依赖于它的输入(@Input),并且传递给它的任何对象都应该被认为是不可变的。这次当我们点击“changeActorProperties”按钮时,视图中没有任何变化。当用户单击该按钮时,将调用方法changeActorProperties并更新actor对象的属性。当更改检测分析绑定到AppComponent模板的属性时,它将看到与以前相同的数据:

    Is slogan !== previousSlogan No, it's the same.
    Is title !== previousTitle? No, it's the same.
    Is actor !== previousActor? No, it's the same.
    

      但是这一次,我们明确地告诉Angular我们的组件只依赖于它的输入,并且它们都是不可变的。然后,Angular假定MovieComponent没有更改,并将跳过对该组件的检查。因为我们没有强制actor对象是不可变的,所以最终我们的模型与视图不同步. 

      让我们重新运行应用程序,但这次我们将单击“更改Actor对象”按钮。这一次,我们正在创建一个Actor类的新实例,并将其分配给this.actor对象。当更改检测分析绑定到AppComponent模板的属性时,它将找到:

    Is slogan !== previousSlogan No, it's the same.
    Is title !== previousTitle? No, it's the same.
    Is actor !== previousActor? Yes, it has changed.
    

      因为更改检测现在知道actor对象已更改(它是一个新实例),它将继续并继续检查MovieComponent的模板以更新其视图。最后,我们的模板和模型是同步的。 

    优缺点对比

      Default:

    1.   优点: 每一次有异步事件发生,Angular都会触发变更检测(脏检查),从根组件开始遍历其子组件,对每一个组件都进行变更检测,对dom进行更新
    2.   缺点: 有很多组件状态(state)没有发生变化,无需进行变更检测,进行没有必要的变更检测,如果你的应用程序中组件越多,性能问题会越来越明显.

      Onpush:

    1.   优点: 组件的变更检测(脏检查)完全依赖于组件的输入(@Input),只要输入值不变,就不会触发变更检测,也不会对其子组件进行变更检测,在组件很多的时候会有明显的性能提升
    2.   缺点:必须保证输入(@Input)是不可变的(可以用Immutable.js解决),就是每一次输入变化都必须是一个新的引用(js中object,array的可变性).

     

  • 相关阅读:
    java-日期转换
    java-Timestamp
    java-判断年份是不是闰年
    Java中Date与String的相互转换
    ORA-01830
    js数组合并
    js清空子节点
    私钥密码
    图片基本样式
    XMLHttpRequest: 网络错误 0x2ee4, 由于出现错误 00002ee4 而导致此项操作无法完成
  • 原文地址:https://www.cnblogs.com/hanshuai/p/9362660.html
Copyright © 2011-2022 走看看