11. 变化监测:Angular提供了数据绑定的功能.所谓的数据绑定就是将组件类的数据和页面的DOM元素关联起来.当数据发生变化时,Angular能够监测到这些变化,并对其所绑定的DOM元素
进行相应的更新,反之亦然.
异步事件的发生会导致组件中的数据变化,但Angular并不是捕捉对象的变化,它采用的是在适当的时机去检验对象的值是被改动.这个时机是由NgZone这个服务去掌控的,它获取到了整个
应用的执行上下文,能够对相关的异步事件发生,完成或者异常等进行捕获,然后驱动Angular的变化监测机制执行.
11.1 数据变化的源头:在应用程序当中,大致有这三种引起数据变化的应用场景.
1.用户的行为操作,即页面操作所引发的用户事件,如click,changes,hover等.
2.前后端的数据交互,如从后端服务拉取页面所需的接口数据,如XMLHttpRequest/WebSocket等.
3.各类定时任务,即在某个延时后再来响应对应的操作.从而对页面数据做出改变,如setTimeout,setInterval,requestAnimationFrame等.
以上三种可能导致数据变化的情景有一个共同的特征,即它们都是异步的处理,使用异步回调函数句柄来处理相关数据操作.因此,任意一个异步操作,都有可能在数据层面上发生改变,
这回导致应用程序的状态被改变.如果可以在每一个异步回调函数执行结束后,通知Angular内核进行变化监测,那么任何数据的更改就可以在视图层实时额反馈出来.
11.2 变动通知机制:通过异步事件来通知Angular进行变化监测,让任何数据的变更可以被实时的反映出来.Angular本身不具备捕获异步事件的机制,通过引入NgZone服务来实现.
NgZone是基于Zone来实现的,NgZone从Zone中fork了一份实例,是Zone派生出的一个子Zone,在Angular环境内注册的异步事件都运行在这个子Zone上.
Zone是如何具备异步事件捕获能力的?Zone以同样的接口,不同的实现方式并替换了一系列与JavaScript的事件相关的标准方法.因此当开发者使用标准接口时,实际上会显调用Zone
的替换方法,再由这些替换方法调用底层的标准方法.这种对上层应用透明的设计,使得在引入Zone的时候,原有代码不需要做太大的改动.
NgZone扩展了一些API并添加了一些可以被订阅的自定义事件,这些自定义事件是Observable流:
1.onUnstable:在Angular单次事件启动前,触发消息通知订阅器.
2.onMicrotaskEmpty:在Zone完成当前Angular单次事件任务时,立即通知订阅者.
3.onStable:在完成onMicrotaskEmpty回调函数之后,在视图变化之前立即通知订阅者,常用来验证应用程序的状态.
由于NgZone只是全局Zone的一个fork,Angular能够决定在Zone内需不需要执行变化监测,如NgZone的runOutsideAngular()方法可以让Angular不执行变化监测.
runOutsideAnglar():即通知NgZone的父Zone在捕获到异步事件时直接返回,从而不在触发自定义的onMicrotaskEmpty事件,直接作用就是不在通知Angular执行变化监测.
针对上面的说明,对变动通知机制可作详细阐述,如:当有异步事件触发导致数据变化时,这些异步事件会被Zones捕获并触发onUnstable自定义事件,在该自定义事件绑定的函数中
来通知Angular去执行变化监测,如当鼠标经的mousemove事件发生时,它将触发变化通知监测.
11.3 变化监测的处理机制:Angular应用由大大小小的组件组成,这些有相互依赖关系的组件组成了一个线性的组件树.此外,没个组件都有一个自己的变化检测器,由此组成的变化检测树.
变化监测树的数据是由上到下单向流动,变化监测的执行总是从根组件开始,从上到下的监测每一个组件的变化.
当一个异步事件发生并导致其中组件数据的改变,在组件中绑定的相关处理事件将会被触发,事件句柄(对象)处理完成相关逻辑之后,NgZone将会执行对应的钩子函数并通知Angular
去执行一次变化监测.
默认情况下,任何一个组件模型中的数据变化都会导致整个组件树的变化监测,但是有很多组件的输入属性是没有变化的,因此没有必要对这些组件进行变化监测操作.减少不必要的监测
操作可以提升应用程序的性能.
11.4 变化监测类:Angular在整个运行期间都会为每个组件创建变化检测类的实例,该实例提供了相关的方法来手动管理变化监测.由于Angular并不知道是哪个组件发生了变化,但是开发者
知道,所以可以给这个组件做一个标记,以此来通知Angular仅仅监测这个组件所在路径上的组件即可.
变化监测类ChangeDelectorRef提供的主要接口如下:
1.markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件.
2.detach():从变化监测树中分离变化监测器,该组件的变化监测器将不在执行变化监测,除非再次手动执行reattact().
3.reattact():把分离的变化监测器重新安装上,使得该组件机器子组件都能执行变化检测.
4.detectChanges():手动触发执行该组件到各个子组件的一次变化监测.
示例如下:
@Component({
selector:'list',
template:`
<ul>
<li *ngFor="let contact of contacts">
<list-item [contact]="contact"></list-item>
</li>
</ul>
`
})
export class ListComponent implements OnInit{
contacts:any={};

//构造器的参数用了语法糖,可以快捷创建一个属cd并绑定到构造函数参数上.
contructor(private cd:ChangeDetectorRef){
//首先将该组件(包含其子组件)从变化监测树中排除出去
cd.detach();
//定时手动执行变化监测,即:每个5秒手动触发一次该组件及其子组件的Angular变化监测
setInterval(()=>{ //这里简化了contacts数据来源代码
this.cd.detectChanges();
},5000);
}

ngOnInit(){
this.getContacts();
}
getContacts(){
this.contacts=data; //这里简化了contacts数据来源代码
}
}
11.4 变化监测策略:@Component中有个可选的元数据changeDetection,它的作用是让开发者定义每个组件的变化监视策略.在使用该功能前,需要先导入ChangeDetectionStrategy
对象.ChangeDetectionStrategy枚举类型值有两种:
1.Default:组件的每次变化监测都会检查其内部所有数据(引用对象会被深度遍历),以此得出数据前后变化;
2.OnPush:组件的变化监测只检查输入属性(@Input修饰的变量)的值是否发生改变,当这个值是引用类型(如Object,Array等)时,则仅对比引用是否改变.
OnPush策略相比Default降低了变化监测的复杂度,对性能提升更好,但是OnPush策略只对比值的'引用',这在某些场景中可能会得不到预期的效果,如果希望组件也能正常更新数
据,解决的办法有两个:
1.使用Default策略,但会牺牲性能.xzm
2.使用Immutable对象来传值,这是比较推荐的做法.
使用Immutable对象可以确保当对象值的引用地址不变时,对象内部的值或结构也会保持不变.反之,当对象内部发生变化时,对象的引用必然发生改变.
示例如下:
//子组件代码
import { Component,Input,ChangeDetectionStragety } from '@angular/core';
@Component({
selector:'list-item',
template:`
<div>
<label> {{contact.get('name')}} </lable>
<span>{{ contact.get('telNum') }}</span>
</div>
`,
changeDetection:ChangeDetectionStragety.OnPush
})
export class ListItemComponet{
@Input() contact:any={};
//...
}
//父组件代码
import { Component } from '@angular/core';
import Immutable from 'immutable';

@Component({
//...
template:`
<list-item [contact]="contactItem"></list-item>
<button (click)="doUpdate()">更新</button>
`,
changeDetection:ChangeDetectionStragety.OnPush
})
export classListComponent{
contactItem:any;
constructor(){
this.contactItem=Immutable.map({
name:'xzm',
telNum:'12345678'
});
}
}

12 组件的其他元数据:
12.1 host:是个功能强大的元数据,主要是用在指令中.通过host指令,可以指定此指令/组件的事件,动作和属性等.
12.2 exportAs:主要是在指令中使用,作用是将指令分配给一个变量,相当于别名.
12.3 moduleId:包含该组件模块的id,它被用于解析模板和样式的相对路径.在Dart中它可以被自动确定,在CommonJS中,它总是被设置为module.id,这中情况下,如果CSS,HTML,TypeScript
文件在同一目录,如app下,则可以去除基准路径,如"app/".
示例:
@Component({
moduleId:module.id,
templateUrl:'some.component.html',
styleUrls:['some.component.css']
})
选择Webpack方案,可以采用"./"开头的相对路径写法,webpack会自动调用require()方法来加载这些模板与样式.
12.4 queries:设置需要被注入到组件的查询.在组件中主要有两种查询,即视图查询和内容查询,它们分别会在ngAfterViewInit和ngAfterContentInit回调函数被调用之前设置.
1.视图查询示例:
//包装一个输入框成组件,实现一单被渲染,则获取焦点.
@Component({
selector:'my-input',
template:`
<input #theInput type="text" />
`,
queries:{
input:new ViewChild('theInput')
}
})
export class MyInput implements AfterViewInit{
input : ElemenetRef=null;

constructor(private renderer:Renderer){}

ngAfterViewInit(){
this.renderer.invokeElementMethod(this.input.nativeElement,'focus');
}
}
以上代码其实相当于如下代码:
@Component({
selector:'my-input',
template:`
<input #theInput type="text" />
`
})
export class MyInput implements AfterViewInit{
@ViewChild('theInput') input:ElemenetRef;

constructor(private renderer:Renderer){}

ngAfterViewInit(){
this.renderer.invokeElementMethod(this.input.nativeElement,'focus');
}
}
2.内容查询:和视图查询类似,不过内容查询是配和ng-content使用的.
示例:
<my-list>
<li *ngFor="#item of items">{{item}}</li>
</my-list>

@Directive({selector:'li'})
export class ListItem{}

@Component({
selector:'my-list',
template:`
<input #theInput type="text" />
`,
queries:{
items:new ContentChildrenn(ListItem) //通过ListItem的选择器绑定li元素
}
})
export class MyInput implements AfterContentInit{
items:new QueryList<ListItem></ListItem>;

constructor(private renderer:Renderer){}

ngAfterContentInit(){

}
}
12.5 animations:animations元数据提供了便捷的动画定义方法,使用方式就和自定义标签一样.animations元数据定义需要先从@angular/core引入一些用于动画的函数,
如下:import {
trigger,
state,
style,
transition,
animate
} from '@angular/core';
定义一个按钮状态动画效果,有"on"和"off"两种状态,默认是"on",点击按钮切换状态,颜色变红,字变小(1.2~1).
示例代码如下:
//...
animations:[
trigger("buttonStatus",[
state('on',style( { color:'#of2', transform:'scale(1.2)' } )),
state('off',style( { color:'f00',transform:'scale(1)' } )),
transition('off=>on',animate('100ms ease-in')),
transition('on=>off',animate('100ms ease-out'))
])
]
有了以上定义的动画效果,就可以在组件模板上通过@triggerName的方式来应用到元素当中,如:
//...
template:`
<div>
<button @buttonStatus="status" (click)="toggleStatus">{{status}}</button>
</div>
`
//...
export class example {
status:string='on';
toggleStatus(){
this.status=(this.status==='on') ? 'off' : 'on';
}
}

4.认识Angular组件之2的更多相关文章

  1. Angular组件——父子组件通讯

    Angular组件间通讯 组件树,1号是根组件AppComponent. 组件之间松耦合,组件之间知道的越少越好. 组件4里面点击按钮,触发组件5的初始化逻辑. 传统做法:在按钮4的点击事件里调用组件 ...

  2. angular,vue,react的基本语法—动态属性、事件绑定、ref,angular组件创建方式

    基本语法: 动态属性: vue: v-bind:attr="msg" :attr="msg" react: attr={msg} angular [attr]= ...

  3. chart.js angular组件封装(ng6)、实战配置、插件编写

    前言 项目需要使用chart.js插件,由于项目是使用angular开发,那么我第一步就是先把chart.js改造成angular组件来使用. 本项目代码都可以在github上下载:项目git地址 a ...

  4. [转]使用 Angular CLI 和 ng-packagr 构建一个标准的 Angular 组件库

    使用 Angular CLI 构建 Angular 应用程序是最方便的方式之一. 项目目标 现在,我们一起创建一个简单的组件库. 首先,我们需要创建一个 header 组件.这没什么特别的,当然接下来 ...

  5. Angular组件之间通讯

    组件之间会有下列3种关系: 1. 父子关系 2. 兄弟关系 3. 没有直接关系 通常采用下列方式处理(某些方式是框架特有)组件间的通讯,如下: 1父子组件之间的交互(@Input/@Output/模板 ...

  6. angular -——组件样式修改不成功

    angular组件样式修改不成功! 自己定义的css可以成功 组件的不行 style在模板字符串里 直接没有 class 是显示的 但是样式不生效 加上面 即可,为什么?我也不太清楚.有知道答案的请回 ...

  7. 手把手教你搭建自己的Angular组件库 - DevUI

    摘要:DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸.灵活.至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠.取悦眼球的设计.如果你正在开发 ToB 的工 ...

  8. angular组件间的通信(父子、不同组件的数据、方法的传递和调用)

    angular组件间的通信(父子.不同组件的数据.方法的传递和调用) 一.不同组件的传值(使用服务解决) 1.创建服务组件 不同组件相互传递,使用服务组件,比较方便,简单,容易.先将公共组件写在服务的 ...

  9. Angular 组件通信的三种方式

    我们可以通过以下三种方式来实现: 传递一个组件的引用给另一个组件 通过子组件发送EventEmitter和父组件通信 通过serive通信 1. 传递一个组件的引用给另一个组件 Demo1 模板引用变 ...

  10. 动态创建angular组件实现popup弹窗

    承接上文,本文将从一个基本的angular启动项目开始搭建一个具有基本功能.较通用.低耦合.可扩展的popup弹窗(脸红),主要分为以下几步: 基本项目结构搭建 弹窗服务 弹窗的引用对象 准备作为模板 ...

随机推荐

  1. Hyperledger Fabric-CA学习

    Hyperleder Fabric系统架构核心逻辑包括MemberShip.Blockchain和Chaincode 其中上述3个核心逻辑中,Membership服务用来管理节点身份.隐私.confi ...

  2. win10安装java

    java安装还比较顺利,贴两篇亲测可行的教程 1.开发环境安装:https://www.cnblogs.com/shirley-0021/p/8510051.html 2.开发工具安装(Eclipse ...

  3. 机器学习笔记(三)Logistic回归模型

    Logistic回归模型 1. 模型简介: 线性回归往往并不能很好地解决分类问题,所以我们引出Logistic回归算法,算法的输出值或者说预测值一直介于0和1,虽然算法的名字有“回归”二字,但实际上L ...

  4. C++面试笔记(3)

    20. 浅拷贝与深拷贝 如何理解C++中的浅拷贝与深拷贝 深拷贝和浅拷贝 在进行对象拷贝时,当对象包含对其他资源的引用,如果需要拷贝这个独享所引用的对象,那就是深拷贝,否则就是浅拷贝 *** 21.构 ...

  5. bottle模板中的替换

    line是模板中一行的内容,类似: {{x}}testinfo{{x+10}} x=10时,模板输出: 10testinfo20 x = 10 splits = re.split(r'\{\{(.*? ...

  6. 服务器tomcat/mysql的一些有关命令

    停服务1.“ps -ef|grep java” # 查看tomcat进程id 若下面出现一大串内容,包含有tomcat的目录,前面的四位数的数字就是tomcat应用的进程id 2.“kill -9 进 ...

  7. 花了2小时写bug

    程序员的工作,写bug,修bug,改bug 写了2小时逻辑关系,没写明白 比昨天多了一个返回上一层的功能 也很简单,清除下数组内容即可 emm..明天继续深究吧 dic = { "植物&qu ...

  8. reat + cesium。 实现 初始化时自动定位,鼠标移动实时展示坐标及视角高度, 淹没分析

    只贴实现淹没分析这块的代码. import styles from './cesium.less'; import React from 'react'; import Cesium from 'ce ...

  9. SpringBoot+POI报表批量导出

    由于servletResponse 获取的输出流对象在一次请求中只能输出一次,所以要想实现批量导出报表,需要将excel文件打包成zip格式然后输出. 好了,废话不多说,上代码. 1. 首先,需要导入 ...

  10. vue2.0 + element-ui 通过vue-cli 脚手架搭建的有关网络安全的项目源代码

    这次是我进入公司的练手项目,是第一次系统学习vue,做出来的demo是比较低端的,而且不太好看,见谅见谅. 源代码下载路径:https://github.com/Pandora-ps/vue-elem ...