我们将创建一个简单的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的可变性).

 

angular 2+ 变化检测系列二(检测策略)的更多相关文章

  1. angular 2+ 变化检测系列一(基础概念)

    什么是变化检测? 变化检测的基本功能就是获取应用程序的内部状态(state),并且是将这种状态对用户界面保持可见.状态可以是javascript中的任何的数据结构,比如对象,数组,(数字,布尔,字符串 ...

  2. angular 2+ 变化检测系列三(Zone.js在Angular中的应用)

    在系列一中,我们提到Zone.js,Zones是一种执行上下文,它允许我们设置钩子函数在我们的异步任务的开始位置和结束位置,Angular正是利用了这一特性从而实现了变更检测. Zones.js非常适 ...

  3. JVM系列二:GC策略&内存申请、对象衰老

    JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...

  4. 【转载】JVM系列二:GC策略&内存申请、对象衰老

    JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...

  5. [转]JVM系列二:GC策略&内存申请、对象衰老

    原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有 ...

  6. Java 设计模式系列(十二)策略模式(Strategy)

    Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...

  7. [知识库分享系列] 二、.NET(ASP.NET)

    最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...

  8. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  9. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

随机推荐

  1. python之路4-文件操作

    对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 f = open('lyrics','r',encoding='utf-8') read_line = f.r ...

  2. 拷贝文件到服务器 提示FTP文件夹错误

    FTP文件夹错误将文件复制到FTP服务器时发生错误.请检查是否有权限将文件放到该服务器上.详细信息:200 Type set to I.227Entering Passive Mode (122,11 ...

  3. ORACLE表数据误删除的恢复方法(提交事务也可以)

    ORACLE表数据误删除的恢复方法(提交事务也可以) 缓存加时间戳 开启行移动功能:ALTER TABLE tablename ENABLE row movement 把表还原到指定时间点:flash ...

  4. ☆ [ZJOI2006] 书架 「平衡树维护数列」

    题目类型:平衡树 传送门:>Here< 题意:要求维护一个数列,支持:将某个元素置顶或置底,交换某元素与其前驱或后继的位置,查询编号为\(S\)的元素的排名,查询排名第\(k\)的元素编号 ...

  5. 皮尔逊相关系数(Pearson Correlation Coefficient, Pearson's r)

    Pearson's r,称为皮尔逊相关系数(Pearson correlation coefficient),用来反映两个随机变量之间的线性相关程度. 用于总体(population)时记作ρ (rh ...

  6. H5与APP混合开发相关知识点总结

    整理一: 现在有这么个需求,如下图 app端点击右上角的 加 号 ,弹出模态框 这个项目是基于vue写的,客户端需要调用H5页面里定义的js方法,但是在vue里,所有的方法都是在组件内部声明的,也只能 ...

  7. Kibana登录认证设置

    Kibana从5.5开始不提供认证功能,想用官方的认证,X-Pack,收费滴 . 所以就自己动手吧,用nginx的代理功能了. 1.安装Nginx: [root@ELK /]# yum -y inst ...

  8. MySQL 导出数据库,出现 “mysqldump: Got error: 1146”

    出现场景 在 cmd 导出数据库时: mysqldump -hlocalhost -uroot -p student_db > C:\student_db.sql 出现: mysqldump: ...

  9. Java EE 开发环境搭建

    1 Windows 1.1 JDK 下载: 下载地址:https://developer.oracle.com/java 安装文件:jdk-8u201-windows-x64.exe JDK 并不是越 ...

  10. Linux中的pipe(管道)与named pipe(FIFO 命名管道)

    catalogue . pipe匿名管道 . named pipe(FIFO)有名管道 1. pipe匿名管道 管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常 ...