angular脏值检测策略

Angular默认情况下保守,当某个节点发生更改时,每次检查每个组件,如下图

我们发现每次变化检测都是从根组件开始,从上往下执行。虽然 Angular 2 优化后的变化检测执行的速度很快,每毫秒几十万次,但我们能否只针对那些有变化的组件才执行变化检测或灵活地控制变化检测的时机呢 ? 答案是有的,接下来我们看一下具体怎么进行优化。

先来一个简单的例子:

  app.component.ts

import { Component } from ‘@angular/core‘;

@Component({
  selector: ‘app-root‘,
  templateUrl: ‘./app.component.html‘
})
export class AppComponent {
  foods = [‘Bacon‘, ‘Lettuce‘, ‘Tomatoes‘];

  addFood(food) {
    this.foods.push(food);
  }
}

  app.component.html

<input #newFood type="text" placeholder="Enter a new food">
<button (click)="addFood(newFood.value)">Add food</button>

<app-child [data]="foods"></app-child>

  child.component.ts

import { Component, Input } from ‘@angular/core‘;

@Component({
  selector: ‘app-child‘,
  templateUrl: ‘./child.component.html‘
})
export class ChildComponent {
  @Input() data: string[];
}

  child.component.html

<ul>
  <li *ngFor="let item of data">{{ item }}</li>
</ul>

这是个很简单的例子,父组件改变值,脏值检测到子组件,每当父组件有改变,子组件也跟着改变,如果父组件的输入属性没有变化的话再检测子组件,这是一件没有意义的事,还会增加性能开销,所以就有了脏值检测策略

OnPush策略

一个简单的例子:

import { Component, Input, ChangeDetectionStrategy } from ‘@angular/core‘;

@Component({
  selector: ‘app-child‘,
  templateUrl: ‘./child.component.html‘,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
  @Input() data: string[];
}

当上面的例子运行的时候,父组件再给foods push新的值得时候,子组件视图也不会更新,这是因为,在js中,默认对象都是可变的,我们先来看一下下面这个例子

var food={
  name:‘小龙坎‘,
  from:‘成都‘
}

var fo=food;
fo.name=‘牛肉面‘;
fo.from=‘兰州‘;
console.log(food===fo);//true

通过这个例子,你也许会明白为什么前面的视图没有更新了,让我们再来了解下Immutable .

var food={
  name:‘小龙坎‘,
  from:‘成都‘
}

var fo=Objec.assign({},food);
fo.name=‘牛肉面‘;
fo.from=‘兰州‘;
console.log(food===fo);//false

Imutable表示对象不可变,我们修改一个对象的时候新建一个对象,在新对象上做修改。

在Onpush策略源码中,它是这么实现对比的:

function looseIdentical(a, b) {
    return a === b || typeof a === ‘number‘ && typeof b === ‘number‘ && isNaN(a) && isNaN(b);
}

所以当我们使用OnPush策略时,要使用Imutable对象才能实现实现我们想要的效果

上面的例子改成这种实现,页面会跟着刷新

addFood(food) {
  this.foods = [...this.foods, food];
}

用OnPush,会大大优化我们项目的性能,

          加了OnPush之后

ChangeDetectorRef

ChangeDetectorRef策略可以完全控制检测。它是通过依赖注入获取对象

import { ChangeDetectorRef } from ‘@angular/core‘;

@Component({}) class ChildComponent {
    constructor(private cdRef: ChangeDetectorRef) {}
}

检测方法有下面几个:

export abstract class ChangeDetectorRef {
  abstract markForCheck(): void;
  abstract detach(): void;
  abstract detectChanges(): void;
  abstract reattach(): void;
}

  API介绍:

  • markForCheck() - 在组件的 metadata 中如果设置了 changeDetection: ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法。
  • detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。
  • reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测
  • detectChanges() - 从该组件到各个子组件执行一次变化检测

ChangeDetectorRef.detectChanges()

假如我们的子组件有个刷新按钮,如下

import { Component,
         Input,
         ChangeDetectionStrategy,
         ChangeDetectorRef } from ‘@angular/core‘;

@Component({
  selector: ‘app-child‘,
  templateUrl: ‘./child.component.html‘,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
  @Input() data: string[];

  constructor(private cd: ChangeDetectorRef) {}

  refresh() {
    this.cd.detectChanges();
  }
}

在每次手动刷新的时候,他会更改检测

ChangeDetectorRef.markForCheck()

如果数据是可观察的

app.component.ts

import { Component } from ‘@angular/core‘;
import { BehaviorSubject } from ‘rxjs/BehaviorSubject‘;

@Component({ ... })
export class AppComponent {
  foods = new BehaviorSubject([‘Bacon‘, ‘Letuce‘, ‘Tomatoes‘]);

  addFood(food) {
    this.foods.next(food);
  }
}

child.component.ts

import { Component,
         Input,
         ChangeDetectionStrategy,
         ChangeDetectorRef,
         OnInit } from ‘@angular/core‘;

import { Observable } from ‘rxjs/Observable‘;

@Component({
  selector: ‘app-child‘,
  templateUrl: ‘./child.component.html‘,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
  @Input() data: Observable<any>;
  foods: string[] = [];

  constructor(private cd: ChangeDetectorRef) {}

  ngOnInit() {
    this.data.subscribe(food => {
      this.foods = [...this.foods, ...food];
    });
  }
}

在子组件订阅了它,在每次值变化的时候进行更新检测

ngOnInit() {
  this.data.subscribe(food => {
    this.foods = [...this.foods, ...food];
    this.cd.markForCheck();
  });
}

下面三张图来说明观察者检测方式:

这时候我们可以脱离的检测,用观察者模式,精准高效的事件了脏值检测,提高整个系统的整体性能。

原文地址:https://www.cnblogs.com/nanguabushuohua/p/9067716.html

时间: 2024-11-20 04:58:51

angular脏值检测策略的相关文章

脏值检测

以前用jquery对页面中的表单元素做验证 绑定事件(表单元素)focus,blur,keydown,keyup,change,click 等等 在事件回调函数中获取需要验证的表单 对表单内容做验证处理(正则) 根据结果显隐提示 提交表单时候我们还会对数据进行拼接(ajax) 注:一般不用submit 自带的提交表单功能 ,因为不利于js管理 在angular中做表单验证只需要添加几个指令即可 在创建表单的时候,angular会根据表单(form , input 等等)标签的name 属性在作用

Angular DirtyChecking(脏值检查) $watch, $apply, $digest

Dirty Checking (脏值检查) Digest cycle and $scope Digest cycle and $scope First and foremost, AngularJS defines a concept of a so-called digest cycle. This cycle can be considered as a loop, during which AngularJS checks if there are any changes to all t

Vue &amp;&amp; Angular 双向绑定检测不到对象属性的添加和删除

由于ES5的限制 Vue  && Angular 双向绑定检测不到对象属性的添加和删除  还有数组增加索引.这些改变不会触发change事件.Vue是因为实例化的时候已经把各个属性都setter和getter进去了. 两个实现的机制不一样.故解决方案也不一样. 详情查看:https://cn.vuejs.org/v2/guide/list.html#对象更改检测注意事项 Angular解决方案: 可以手动调用$apply()方法,会再自动轮询一遍 Vue解决方案: 使用vm.$set(对象

angularjs 做不到实时脏值查询

angularjs 做不到脏值查询 ,数据请求过来,不操作其他按钮,请求的值就是展示不出来:(相当于,只有手动触发,angularjs内部才会把脏值查询出来): 解决办法:在请求过来的值旁边加上$scope.apply(),这样数据就能直接展示出来了 举例说明 success:function(data){ $scope.msg=data; $scope.$apply(); } 或者: success:function(data){ $scope.$apply(function(){ $scop

Angular:OnPush变化检测策略介绍

在OnPush策略下,Angular不会运行变化检测(Change Detection ),除非组件的input接收到了新值.接收到新值的意思是,input的值或者引用发生了变化.这样听起来不好理解,看例子: 子组件接收一个balls(别想歪:))输入,然后在模板遍历这个balls数组并展示出来.初始化2秒后,往balls数组push一个new ball: //balls-list.component.ts @Component({ selector: 'balls-list', templat

angular脏检查机制

机制: 是angular中双向数据绑定的原理,是通过一个原先的旧值和当前的新值作比较是否相等,如果相等则不会触发脏检查机制,如果不相等则会触发脏检查机制,$$watcher进行监听,触发新老值的替换. 在angular的程序中,一个以{{}}或者指令绑定的视图,就会在此绑定的视图上有一个$watch事件,将数据中最新的值呈现在界面上,所有的watch储存在$$warcher中,一次脏检查就是调用已从$spply()或者$digest(),遍历检查所有watch,将数据中最新值呈现在桌面上. $$

leetcode-峰值检测

寻找峰值 峰值元素是指其值大于左右相邻值的元素. 给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引. 数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可. 你可以假设 nums[-1] = nums[n] = -∞. 示例 1: 输入: nums = [1,2,3,1] 输出: 2 解释: 3 是峰值元素,你的函数应该返回其索引 2. 示例 2: 输入: nums = [1,2,1,3,5,6,4] 输出: 1 或 5 解释: 你的

C#对文件进行MD5值检测

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.ComponentModel; 7 using System.Data; 8 using System.Drawing; 9 using System.IO; 10 using System.Security.Cryp

angular的滚动检测和窗口大小变化检测

滚动检测 // 监听滚动事件 scrolling: any ngOnInit() { this.scrolling = fromEvent(window, 'scroll') // fromEvent(element,event) .subscribe((event) => { // 订阅 this.onScroll() }) } onScroll () { this.scrollTop = document.documentElement.scrollTop this.elementScrol