Home  >  Article  >  Web Front-end  >  Detailed interpretation of change detection issues in the Angular series

Detailed interpretation of change detection issues in the Angular series

亚连
亚连Original
2018-06-04 14:39:391416browse

This article mainly introduces the detailed explanation of Change Detection in the Angular series. Now I share it with you and give it as a reference.

Overview

# Simply put, change detection is used by Angular to detect whether the value bound between the view and the model has occurred. Change, when it is detected that the value bound in the model changes, it is synchronized to the view. On the contrary, when it is detected that the value bound in the view changes, the corresponding binding function is called back.

Under what circumstances will change detection be caused?

To sum up, there are mainly the following situations that may also change the data:

  1. User input operations, such as click, submit, etc.

  2. Request server data (XHR)

  3. Timing events, such as setTimeout, setInterval

The above three situations all have one thing in common, that is, the events that cause the binding value to change occur asynchronously. If these asynchronous events can notify the Angular framework when they occur, then the Angular framework can detect changes in time.

The left side represents the code to be run. The stack here represents the running stack of Javascript, and webApi is some Javascript API provided by the browser. TaskQueue represents the task in Javascript. Queue, because Javascript is single-threaded, asynchronous tasks are executed in a task queue.

Specifically, the operating mechanism of asynchronous execution is as follows:

  1. All synchronous tasks are executed on the main thread, forming an execution context stack.

  2. Besides the main thread, there is also a "task queue". As long as the asynchronous task has running results, an event is placed in the "task queue".

  3. Once all synchronization tasks in the "execution stack" have been executed, the system will read the "task queue" to see what events are in it. Those corresponding asynchronous tasks end the waiting state, enter the execution stack, and start execution.

  4. The main thread keeps repeating the third step above.

When the above code is executed in Javascript, first func1 enters the running stack. After func1 is executed, setTimeout enters the running stack. During the execution of setTimeout, the callback function cb is added to the task queue. Then setTimeout pops out of the stack, and then executes the func2 function. When the func2 function is executed, the running stack is empty, and then cb in the task queue enters the running stack and is executed. It can be seen that the asynchronous task will first enter the task queue. When all the synchronous tasks in the running stack have been executed, the asynchronous task will enter the running stack and be executed. If some hook functions can be provided before and after the execution of these asynchronous tasks, Angular can know the execution of the asynchronous tasks through these hook functions.

angular2 Get change notification

Then the question is, how does angular2 know that the data has changed? How do you know where the DOM needs to be modified and how to modify the DOM in the smallest possible range? Yes, modify the DOM as small as possible, because manipulating the DOM is a luxury for performance.

In AngularJS, it is triggered by the code $scope.$apply() or $scope.$digest, and Angular is connected to ZoneJS, which monitors all asynchronous events of Angular.

How does ZoneJS do it?

Actually, Zone has something called monkey patching. When Zone.js is running, a layer of proxy packaging will be made for these asynchronous events. That is to say, after Zone.js is running, when browser asynchronous events such as setTimeout and addEventListener are called, the native methods are no longer called, but are called. Monkey patched proxy method. Hook functions are setup in the agent. Through these hook functions, you can easily enter the context of asynchronous task execution.

//以下是Zone.js启动时执行逻辑的抽象代码片段
function zoneAwareAddEventListener() {...}
function zoneAwareRemoveEventListener() {...}
function zoneAwarePromise() {...}
function patchTimeout() {...}
window.prototype.addEventListener=zoneAwareAddEventListener;
window.prototype.removeEventListener=zoneAwareRemoveEventListener;
window.prototype.promise = zoneAwarePromise;
window.prototype.setTimeout = patchTimeout;

Change detection Process

#The core of Angular is componentization, and the nesting of components will eventually form a component tree. Angular's change detection can be divided into components. Each Component corresponds to a changeDetector. We can obtain the changeDetector through dependency injection in the Component. Our multiple Components are organized in a tree structure. Since one Component corresponds to a changeDetector, the changeDetectors are also organized in a tree structure.

In addition, Angular’s ​​data flow is from the top. , one-way flow from parent component to child component. Unidirectional data flow ensures efficient and predictable change detection. Although after checking the parent component, the child component may change the parent component's data so that the parent component needs to be checked again. This is not a recommended way of handling data. In development mode, Angular will perform a secondary check. If the above situation occurs, the secondary check will report an error: Expression Changed After It Has Been Checked Error. In a production environment, dirty checking is only performed once.

相比之下,AngularJS采用的是双向数据流,错综复杂的数据流使得它不得不多次检查,使得数据最终趋向稳定。理论上,数据可能永远不稳定。AngularJS给出的策略是,脏检查超过10次,就认为程序有问题,不再进行检查。

变化检测策略

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 = &#39;change detection&#39;;
 title: string = &#39;default 策略&#39;;
 star: Star = new Star(&#39;周&#39;, &#39;杰伦&#39;);
 changeStar() {
  this.star.firstName = &#39;吴&#39;;
  this.star.lastName = &#39;彦祖&#39;;
 }
 changeStarObject() {
  this.star = new Star(&#39;刘&#39;, &#39;德华&#39;);
 } 
}

movie.component.ts

@Component({
 selector: &#39;movie&#39;,
 styles: [&#39;p {border: 1px solid black}&#39;],
 template: `
<p>
<h3>{{ title }}</h3>
<p>
<label>Star:</label>
<span>{{star.firstName}} {{star.lastName}}</span>
</p>
</p>`,

})
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
 ){}

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

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

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

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

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

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

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

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

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

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

![图片描述][3]

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

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

输入属性为Observable

修改app.component.ts

@Component({
 selector: &#39;app-root&#39;,
 template: `
 <h1>变更检测策略</h1>
 <p>{{ slogan }}</p>
 <button type="button" (click)="changeStar()"> 改变明星属性
 </button>
 <button type="button" (click)="changeStarObject()">
   改变明星对象
 </button>
 <movie [title]="title" [star]="star" [addCount]="count"></movie>`,
})
export class AppComponent implements OnInit{
 slogan: string = &#39;change detection&#39;;
 title: string = &#39;OnPush 策略&#39;;
 star: Star = new Star(&#39;周&#39;, &#39;杰伦&#39;);
 count:Observable<any>;

 ngOnInit(){
  this.count = Observable.timer(0, 1000)
 }
 changeStar() {
  this.star.firstName = &#39;吴&#39;;
  this.star.lastName = &#39;彦祖&#39;;
 }
 changeStarObject() {
  this.star = new Star(&#39;刘&#39;, &#39;德华&#39;);
 } 
}

此时,有两种方式让MovieComponent进入检测,一种是使用变化检测对象中的 markForCheck()方法.

ngOnInit() {
  this.addCount.subscribe(() => {
   this.count++;
   this.changeRef.markForCheck();
  })

另外一种是使用async pipe 管道

@Component({
 selector: &#39;movie&#39;,
 styles: [&#39;p {border: 1px solid black}&#39;],
 template: `
<p>
<h3>{{ title }}</h3>
<p>
<button (click)="changeStar()">点击切换名字</button>    
<label>Star:</label>
<span>{{star.firstName}} {{star.lastName}}</span>
</p>
<p>{{addCount | async}}</p>
</p>`,
 changeDetection: ChangeDetectionStrategy.OnPush
})

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

利用vue实现如何在表格里,取每行的id(详细教程)

利用vue移动端UI框架如何实现QQ侧边菜单(详细教程)

使用vue及element组件的安装教程(详细教程)

The above is the detailed content of Detailed interpretation of change detection issues in the Angular series. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn