• Angular: 变化检测策略Change Detection的比较


    前置笔记:

    NGZORRO:一种Angular页面性能优化手段_董厂长的博客-CSDN博客事情是这样的,今天在看NGZORRO时候看见这么一句话。OnPush模式,性能卓越。🤔那我就来看看antdesgin是怎么做出性能优化的。所以“this.cdRef.markForCheck();”这句话只能引起一次变化检测,如要多次,则要多次的运行这句话,如果以上代码功能要改成3秒后开始计时,只要将this.timer == 3改成this.timer >= 3就行了。...https://blog.csdn.net/dongnihao/article/details/125973814?spm=1001.2014.3001.5501

    ChangeDetectionStrategy.Default 默认模式

    ChangeDetectionStrategy.OnPush  OnPush模式 (NGZORRO使用的优化手段)

     简单来说:默认模式下,一旦检测到父级组建的变化,更新所有的子组件。

    这边举个例子:

    1. @Component({
    2. template: `

      I am { { data.name } } and I live in { { data.address } }

    3. `
    4. })
    5. export class CDParentComponent {
    6. data: any = {
    7. name: 'James',
    8. address: 'ShangHai',
    9. contact: {
    10. email: 'XXX@gmail.com',
    11. phone: '1234567890'
    12. }
    13. };
    14. changeInfo() {
    15. this.data.contact.email = 'update@gmail.com';
    16. this.data.contact.phone = '00000000';
    17. this.data.name = 'Kobe';
    18. }
    19. }

    其子组件:

    1. @Component({
    2. selector: "cd-child",
    3. template: `

      here is email in the child: { { data.contact.email } }

      `
      ,
    4. changeDetection: ChangeDetectionStrategy.OnPush
    5. })
    6. export class CDChildComponent implements OnChanges {
    7. @Input() data: any;
    8. ngOnChanges() {
    9. console.log('data has been changed: ' + this.data.name + ' ' + this.data.address);
    10. }
    11. }

    我们点击 Change Info 按钮,不会触发 CDChildComponent 中的变化检测,页面 email 也不会有变化。 

    以下四种情况还是可以触发该组件的变化检测:

    1. 组件的@Input引用发生变化。

    2. 组件的 DOM 事件,包括它子组件的 DOM 事件,比如 click、submit、mouse down。

    3. Observable 订阅事件,同时设置 Async pipe。

    4. 利用以下方式手动触发变化检测:

      • ChangeDetectorRef.detectChanges
      • ChangeDetectorRef.markForCheck()
      • ApplicationRef.tick()

    重点说说@input引用的变化

    好好想想,自己是不是更新子组件时候写过这种代码,知不知道这么写的原因啊! :

    fuckArray = [...anotherFcukArray];

    必须是 @Input 的引用发生改变才会触发变化检测,并且仅限于 @Input 的变化检测,在 OnPush 策略下,会触发组件的变化检测。在这里先解释一下 JS 中的数据类型,在 JS 中有七种数据类型,其中包括六中原始类型(primitive values)和 Object。

    六种原始类型分别为:Boolean、Null、Undefined、Number、String、Symbol (ECMAScript 6 新定义)。

    除了 Object 以外的所有类型(即原始类型)都是不可变的(Immutable),是通过值传递的,每次对它们的改动都会在内存里生成一个新的值。而 Object 是通过引用传递的,每次对 Object 改动,引用不会改变。

    在上面的示例代码CDParentComponent中的changeInfo方法如下:

    1. changeInfo() {
    2. this.data.contact.email = 'update@gmail.com';
    3. this.data.contact.phone = '00000000';
    4. this.data.name = 'BigFool';
    5. }

    data 是一个对象,在changeInfo方法里通过如上方式改变 email 的值。同时在 CDChildComponent 设置了 OnPush,虽然@Input data的属性 eamil 发生变化但是 data 对象的引用并没有改变,并不会触发 CDChildComponent 中的变化检测,页面的 eamil 也不会发生变化。

    如果把 CDParentComponent 中的changeInfo方法改成下面这样:

    1. changeInfo() {
    2. this.data = {
    3. name: 'Curry', address: 'ShangHai',
    4. contact: {
    5. email: 'update@gmail.com',
    6. phone: '1234567890'
    7. }
    8. };
    9. }

    这时候点击 Change Info 按钮,触发了变化检测,页面的 email 被更新了:

    这种方式在改变 data 对象 email 值同时也改变了对象的引用。这时组件的 @Input 引用发生变化,虽然加了 OnPush 但 @Input 的变化检测还是会被触发。

    再重点说说手动触发更新

    在 OnPush 策略下,手动调用这三种方式会触发变化检测:

    • ChangeDetectorRef.detectChanges
    • ChangeDetectorRef.markForCheck()
    • ApplicationRef.tick()

    要 依赖 注入 进去 才可以!不要像弱智一样粘贴复制一下方法就仍到代码里面!

    1. @Component({
    2. selector: "cd-child",
    3. template: `

      here is email in the child: { { data.contact.email } }

    4. here is the counter triggered manually in the child: { { counter } }

    5. `,
  • changeDetection: ChangeDetectionStrategy.OnPush
  • })
  • export class CDChildComponent implements OnInit, OnChanges {
  • @Input() data: any;
  • counter: number = 1;
  • count$: Observable<number>;
  • constructor(
  • // 看这里 傻逼
  • private cd: ChangeDetectorRef
  • ) { }
  • ngOnInit() {
  • setInterval(() => {
  • this.counter = this.counter + 5;
  • // 还有这里 傻逼
  • this.cd.detectChanges();
  • }, 1000);
  • }
  • ngOnChanges() {
  • console.log('data has been changed: ' + this.data.name + ' ' + this.data.address);
  • }
  • }
  • ChangeDetectorRef.markForCheck()效果跟 detectChanges 是一样的,只不过 detectChanges 会立马触发当前组件和它子组件变化检测。markForCheck 并不会立马触发变化检测,而是标记需要被变化检测,在当前或下一轮的变化检测中被触发。

    ApplicationRef.tick() 触发整个应用的组件树从上到下执行变化检测。

                  

  • 相关阅读:
    Mysql事务
    力扣-345.反转字符串中的元音字母
    SonarQube学习笔记三:直接使用sonar-scanner扫描器
    mac怎么把两张图片拼在一起
    常见集群算法解析
    【精讲】vue v-if的demo案例、v-show案例、v-for循环遍历案例
    【STM32 CubeMX】I2C查询方式
    Maven-快速入门教程
    自动驾驶仿真:角雷达坐标系转换详解
    mysql忘记密码怎么办(附免密登录和修改密码)
  • 原文地址:https://blog.csdn.net/dongnihao/article/details/126300000