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实例。下面的代码片段是AppComponent_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属性”按钮时会发生什么。更改由事件触发,更改的传播分两个阶段完成:应用阶段和变更检测阶段.
- 阶段一(应用阶段) 在第一阶段,应用程序(我们的代码)负责更新模型以响应某些事件。在此方案中,属性actor.firstName和actor.lastName已更新。
- 阶段一(变更检测阶段) 现在我们的模型已更新,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:
- 优点: 每一次有异步事件发生,Angular都会触发变更检测(脏检查),从根组件开始遍历其子组件,对每一个组件都进行变更检测,对dom进行更新
- 缺点: 有很多组件状态(state)没有发生变化,无需进行变更检测,进行没有必要的变更检测,如果你的应用程序中组件越多,性能问题会越来越明显.
Onpush:
- 优点: 组件的变更检测(脏检查)完全依赖于组件的输入(@Input),只要输入值不变,就不会触发变更检测,也不会对其子组件进行变更检测,在组件很多的时候会有明显的性能提升
- 缺点:必须保证输入(@Input)是不可变的(可以用Immutable.js解决),就是每一次输入变化都必须是一个新的引用(js中object,array的可变性).
angular 2+ 变化检测系列二(检测策略)的更多相关文章
- angular 2+ 变化检测系列一(基础概念)
什么是变化检测? 变化检测的基本功能就是获取应用程序的内部状态(state),并且是将这种状态对用户界面保持可见.状态可以是javascript中的任何的数据结构,比如对象,数组,(数字,布尔,字符串 ...
- angular 2+ 变化检测系列三(Zone.js在Angular中的应用)
在系列一中,我们提到Zone.js,Zones是一种执行上下文,它允许我们设置钩子函数在我们的异步任务的开始位置和结束位置,Angular正是利用了这一特性从而实现了变更检测. Zones.js非常适 ...
- JVM系列二:GC策略&内存申请、对象衰老
JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...
- 【转载】JVM系列二:GC策略&内存申请、对象衰老
JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...
- [转]JVM系列二:GC策略&内存申请、对象衰老
原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有 ...
- Java 设计模式系列(十二)策略模式(Strategy)
Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...
- [知识库分享系列] 二、.NET(ASP.NET)
最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- 手牵手,从零学习Vue源码 系列二(变化侦测篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...
随机推荐
- error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\x86_amd64\\cl.exe' failed with exit status 2
安装mysql是出现这个错误. python3.和python2.两个的版本不一样,所以安装的东西也不一样:MySQLdb 安装mysql的连接包.工具安装 Python3.x版本:Pip insta ...
- Django+Vue打造购物网站(五)
注册和登陆 drf的认证 http://www.django-rest-framework.org/api-guide/authentication/ settings.py文件的配置 INSTALL ...
- Linux systemctl 命令完全指南
Systemctl是一个systemd工具,主要负责控制systemd系统和服务管理器. Systemd是一个系统管理守护进程.工具和库的集合,用于取代System V初始进程.Systemd的功能是 ...
- CF209C Trails and Glades
题目链接 题意 有一个\(n\)个点\(m\)条边的无向图(可能有重边和自环)(不一定联通).问最少添加多少条边,使得可以从\(1\)号点出发,沿着每条边走一遍之后回到\(1\)号点. 思路 其实就是 ...
- svn客户端更改用户名
你是用的小乌龟做客户端吗?在文件夹里点右键,选择TortoiseSVN->Setings->SavedData里面有个authentication data,点击后面的Clear就好了下次 ...
- Pandas系列(三)-缺失值处理
内容目录 1. 什么是缺失值 2. 丢弃缺失值 3. 填充缺失值 4. 替换缺失值 5. 使用其他对象填充 数据准备 import pandas as pd import numpy as np in ...
- 前向分步算法 && AdaBoost算法 && 提升树(GBDT)算法 && XGBoost算法
1. 提升方法 提升(boosting)方法是一种常用的统计学方法,在分类问题中,它通过逐轮不断改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性能 0x1: 提升方法的基本 ...
- 第七节:WebApi与Unity整合进行依赖注入和AOP的实现
一. IOC和DI 1. 通过Nuget引入Unity程序集. PS:[版本:5.8.6] 2. 新建DIFactory类,用来读取Unity的配置文件并创建Unity容器,需要注意的是DIFacto ...
- Node.js实战项目学习系列(4) node 对象(global、process进程、debug调试)
前言 在之前的课程我们学习了Node的模块化规则,接下来我们将学习下 Node的几个新特性:global ,process进程,debug调试 global 跟在浏览器中的window一样都是全局变量 ...
- 深入浅出mybatis之返回主键ID
目录 添加单一记录时返回主键ID 在映射器中配置获取记录主键值 获取新添加记录主键字段值 添加批量记录时返回主键ID 获取主键ID实现原理 添加记录后获取主键ID,这是一个很常见的需求,特别是在一次前 ...