详解Angular系列之变化检测(Change Detection)(2)

Angular有两种变化检测策略。Default是Angular默认的变化检测策略,也就是上述提到的脏检查,只要有值发生变化,就全部从父组件到所有子组件进行检查,。另一种更加高效的变化检测方式:OnPush。OnPush策略,就是只有当输入数据(即@Input)的引用发生变化或者有事件触发时,组件才进行变化检测。

defalut 策略

main.component.ts

@Component({ selector: 'app-root', template: ` <h1>变更检测策略</h1> <p>{{ slogan }}</p> <button type="button" (click)="changeStar()"> 改变明星属性 </button> <button type="button" (click)="changeStarObject()"> 改变明星对象 </button> <movie [title]="title" [star]="star"></movie>`, }) export class AppComponent { slogan: string = 'change detection'; title: string = 'default 策略'; star: Star = new Star('周', '杰伦'); changeStar() { this.star.firstName = '吴'; this.star.lastName = '彦祖'; } changeStarObject() { this.star = new Star('刘', '德华'); } }

movie.component.ts

@Component({ selector: 'movie', styles: ['div {border: 1px solid black}'], template: ` <div> <h3>{{ title }}</h3> <p> <label>Star:</label> <span>{{star.firstName}} {{star.lastName}}</span> </p> </div>`, }) export class MovieComponent { @Input() title: string; @Input() star; }

上面代码中, 当点击第一个按钮改变明星属性时,依次对slogan, title, star三个属性进行检测, 此时三个属性都没有变化, star没有发生变化,是因为实质上在对star检测时只检测star本身的引用值是否发生了改变,改变star的属性值并未改变star本身的引用,因此是没有发生变化。

而当我们点击第二个按钮改变明星对象时 ,重新new了一个 star ,这时变化检测才会检测到 star发生了改变。

然后变化检测进入到子组件中,检测到star.firstName和star.lastName发生了变化, 然后更新视图.

OnPush策略

与上面代码相比, 只在movie.component.ts中的@component中增加了一行代码:

changeDetection:ChangeDetectionStrategy.OnPush
此时, 当点击第一个按钮时, 检测到star没有发生变化, ok,变化检测到此结束, 不会进入到子组件中, 视图不会发生变化.

当点击第二个按钮时,检测到star发生了变化, 然后变化检测进入到子组件中,检测到star.firstName和star.lastName发生了变化, 然后更新视图.

所以,当你使用了OnPush检测机制时,在修改一个绑定值的属性时,要确保同时修改到了绑定值本身的引用。但是每次需要改变属性值的时候去new一个新的对象会很麻烦,immutable.js 你值得拥有!

变化检测对象引用

通过引用变化检测对象ChangeDetectorRef,可以手动去操作变化检测。我们可以在组件中的通过依赖注入的方式来获取该对象:

constructor( private changeRef:ChangeDetectorRef ){}

变化检测对象提供的方法有以下几种:

markForCheck() - 在组件的 metadata 中如果设置了 changeDetection:ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法, 该方法的意思是在变化监测时必须检测该组件。

detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。

reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测

detectChanges() - 从该组件到各个子组件执行一次变化检测

OnPush策略下手动发起变化检测

组件中添加事件改变输入属性

在上面代码movie.component.ts中修改如下

@Component({ selector: 'movie', styles: ['div {border: 1px solid black}'], template: ` <div> <h3>{{ title }}</h3> <p> <button (click)="changeStar()">点击切换名字</button> <label>Star:</label> <span>{{star.firstName}} {{star.lastName}}</span> </p> </div>`, changeDetection:ChangeDetectionStrategy.OnPush }) export class MovieComponent { constructor( private changeRef:ChangeDetectorRef ){} @Input() title: string; @Input() star; changeStar(){ this.star.lastName = 'xjl'; } }

此时点击按钮切换名字时,star更改如下

![图片描述][3]

第二种就是上面讲到的使用变化检测对象中的 markForCheck()方法.

ngOnInit() { setInterval(() => { this.star.lastName = 'xjl'; this.changeRef.markForCheck(); }, 1000); }

输入属性为Observable

修改app.component.ts

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/pssyd.html