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

58 篇文章 3 订阅

前置笔记:

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使用的优化手段)

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

这边举个例子:

@Component({
    template: `<h1>I am { { data.name } } and I live in { { data.address } } </h1>

               <cd-child [data]="data"></cd-child>
   
               <button (click)="changeInfo()">Change Info</button>`
})
export class CDParentComponent {
    data: any = {
        name: 'James',
        address: 'ShangHai',
        contact: {
            email: 'XXX@gmail.com',
            phone: '1234567890'
        }
    };
    changeInfo() {
        this.data.contact.email = 'update@gmail.com';
        this.data.contact.phone = '00000000';
        this.data.name = 'Kobe';
    }

}

其子组件:

@Component({
    selector: "cd-child",
    template: `<h3>here is email in the child: { { data.contact.email } } </h3>`,
    changeDetection: ChangeDetectionStrategy.OnPush
})

export class CDChildComponent implements OnChanges {
    @Input() data: any;

    ngOnChanges() {
        console.log('data has been changed: ' + this.data.name + ' ' + this.data.address);
    }
}

我们点击 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方法如下:

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

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

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

    changeInfo() {
        this.data = {
            name: 'Curry', address: 'ShangHai',
            contact: {
                email: 'update@gmail.com',
                phone: '1234567890'
            }
        };
    }

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

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

再重点说说手动触发更新

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

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

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

@Component({
    selector: "cd-child",
    template: `<h3>here is email in the child: { { data.contact.email } } </h3>
                <h3>here is the counter triggered manually in the child: { { counter } } </h3> 
               <div style="margin-bottom:10px;">
                    <button (click)="changeCounter()">change child counter</button>
                </div>`,
    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() 触发整个应用的组件树从上到下执行变化检测。

              

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

董厂长

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

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

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

打赏作者

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

抵扣说明:

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

余额充值