【Angular】中的变化检测(ChangeDetection)

ChangeDetection

概述

简单来说变化检测就是Angular用来检测视图与模型之间绑定的值是否发生了改变,当检测到模型中绑定的值发生改变时,则同步到视图上,反之,当检测到视图上绑定的值发生改变时,则回调对应的绑定函数。

当组件实例化之后,Angular 就会创建一个变更检测器,它负责传播组件各个绑定值的变化。 该策略有下列值:

changeDetection:ChangeDetectionStrategy.Default

默认策略,执行脏检查,只要值发生变化,就从父组件到子组件进行全面变更检测。检测全面,但当组件很多时,检测效率低。

changeDetection:ChangeDetectionStrategy.OnPush

当输入数据@Input的引用发生变化或者有事件触发时,组件才进行变更检测。检测不全面,但检测效率高。

变更检测的概念

组件内的数据状态变化以后,需要对应更新视图。这种将视图和数据同步的机制,就叫变化检测。核心永远都是旧数据和新数据的对比。

变更检测的触发条件

只要发生了异步操作(Events, Timer, XHR),Angular 就会认为有状态可能发生变化了,然后就会进行变更检测。

  • Events::click,mouseover,mouseout,keyup,keydown 等浏览器事件;
  • Timer:setTimeout/setInterval;
  • XHR:各类请求等。

既然都是对异步操作进行变更检测,那么Angular是如何订阅异步请求,进行变更检测的呢?

这里介绍下NgZone以及它的fork对象Zone.js。

Zone.js 用于封装和拦截浏览器中的异步活动、它还提供 异步生命周期的钩子 和 统一的异步错误处理机制。

Zone.js 是通过 Monkey Patch(猴子补丁) 的方式来对浏览器中的常见方法和元素进行拦截,例如 setTimeout 和 HTMLElement.prototype.onclick。Angular 在启动时会利用 Zone.js 修补几个低级浏览器 API,从而实现异步事件的捕获,并在捕获时间后调用变更检测。

Angular通过forkZone.js并拓展出一个自己的区域NgZone,让应用中的所有异步操作都会运行在这个区域中。

Angular的变更检测如何工作的?

Angualr会为每一个组件生成一个变化监测器changeDetector ,记录组件的变化状态。

我们在创建了一个Angular 应用后,Angular 会同时创建一个 ApplicationRef 的实例,这个实例代表的就是我们当前创建的这个 Angular 应用的实例。 ApplicationRef 创建的同时,会订阅 ngZone 中的 onMicrotaskEmpty 事件,在所有的微任务完成后,遍历并调用所有的视图的detectChanges()来执行变更检测。

变更检测的执行顺序
  1. 更新所有子子组件绑定的属性
  2. 调用所有子组件生命周期的钩子 OnChanges, OnInit, DoCheck,AfterContentInit
  3. 更新当前组件的DOM
  4. 调用子组件的变更检测(即所有子组件循环上述3步)
  5. 调用所有子组件的生命周期钩子 ngAfterViewInit
变更检测的执行策略
  1. Default 策略
    每次 事件 触发 变化检测(如用户事件、计时器、XHR、promise 等)时,此默认策略都会从上到下检查组件树中的每个组件。这种对组件的依赖关系不做任何假设的保守检查方式称为 脏检查,这种策略在我们应用组件过多时会对我们的应用产生性能的影响。

  2. OnPush 策略
    修改组件装饰器的changeDetection,设置为 OnPush 策略后,Angular 每次触发变化检测后会跳过该组件和该组件的所以子组件变化检测。

@Component({
  selector: 'app-xxx',
  templateUrl: 'XXX',
  styleUrls: ['XXX'],
  changeDetection:ChangeDetectionStrategy.OnPush
})

在 OnPush 策略下,只有以下这几种情况才会触发组件的变化检测:

  • 输入值(@Input)更改(入input的值必须是一个新的引用)

  • 当前组件或子组件之一触发了事件 (但在onPush策略中,以下操作不会触发变更检测)

    • setTimeout()
    • setInterval()
    • Promise.resolve().then()
    • this.http.get(‘…’).subscribe()
  • 手动触发变更检测(每个组件都会关联一个组件视图ChangeDetectorRef)

    • detectChanges(): 它会触发当前组件和子组件的变化检测

    • markForCheck():它不会触发变化检测,但是会把当前的OnPush组件和所以的父组件为OnPush的组件 标记为需要检测状态,在当前或者下一个变化检测周期进行检测

    • ApplicationRef.tick() :它会根据组件的变化检测策略,触发整个应用程序的更改检测
      在这里插入图片描述

    • async pipe

参考链接:https://juejin.cn/post/7093770971588329508

ChangeDetectorRef

概述

实时检测数据的变化并更新视图数据

它是Angular 各种视图的基础类,提供变更检测功能。 变更检测树会收集要检查的所有视图。 使用这些方法从树中添加或移除视图、初始化变更检测并显式地把这些视图标记为脏的,意思是它们变了、需要重新渲染。

它的五个方法:

  1. markForCheck(): 当视图使用 OnPush变更检测策略时,把该视图显式标记为已更改,以便它再次进行变更检测。
  2. detach(): 从变更检测树中分离开视图。 已分离的视图在重新附加上去之前不会被检查。 与 detectChanges() 结合使用,可以实现局部变更检测。
  3. detectChanges(): 检查该视图及其子视图。与 detach 结合使用可以实现局部变更检测。
  4. checkNoChanges(): 检查变更检测器及其子检测器,如果检测到任何更改,则抛出异常。
  5. reattach(): 把以前分离开的视图重新附加到变更检测树上。 视图会被默认附加到这棵树上。

使用:

  1. 引入ChangeDetectorRef模块
  2. 声明
  3. 使用
import { ChangeDetectionStrategy,ChangeDetectorRef } from '@angular/core';
 
@component({
    selector: 'xxx',
    exportAs: 'xxx',
    changeDetection: ChangeDetectionStrategy.OnPush
})
 
constructor(
    private: cdr:ChangeDetectorRef
){}
 
haha(){
    this.cdr.markForCheck(); //需要时调用
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMSI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值