我们将创建一个简单的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. [Linux] Vim 撤销 回退 操作

    在vi中按u可以撤销一次操作 u   撤销上一步的操作      Ctrl+r 恢复上一步被撤销的操作 注意:        如果你输入“u”两次,你的文本恢复原样,那应该是你的Vim被配置在Vi兼容 ...

  3. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  4. koa-router 后台路由管理框架

    koa-router是koa框架配套的路由管理模块,对后台的接口分离出来. 首先引入koa和koa-router, 然后分批设置路由: 代码中的institution.modifyInsStatus是 ...

  5. SQL 中左连接与右链接的区别

    在微信公众号中看到的sql左连接与右链接的总结,这个图总结的很好,所以单独收藏下:

  6. php中fastcgi和php-fpm是什么东西

    参考和学习了以下文章: 1. mod_php和mod_fastcgi和php-fpm的介绍,对比,和性能数据 2. 实战Nginx_取代 为了如何一步步的引出fastcgi和php-fpm,我先一点一 ...

  7. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

  8. BZOJ 3613: [Heoi2014]南园满地堆轻絮(二分)

    题面: https://www.lydsy.com/JudgeOnline/problem.php?id=3613 题解: 考虑前面的数越小答案越优秀,于是我们二分答案,判断时让前面的数达到所能达到的 ...

  9. 1: jsp的4个作用域 pageScope、requestScope、sessionScope、applicationScope的区别

    1.区别: 1.page指当前页面有效.在一个jsp页面里有效 2.request 指在一次请求的全过程中有效,即从http请求到服务器处理结束,返回响应的整个过程,存放在HttpServletReq ...

  10. ES6部分知识点总结

    注:本文通过yck前端面试小册学习整理而得,记录下来供自己查阅 1.var 变量提升 使用var声明的变量,声明会被提升到作用域的顶部 举几个例子: eg1: console.log(a) // un ...