Maison > interface Web > js tutoriel > Interprétation détaillée des problèmes de détection de changement dans la série Angular

Interprétation détaillée des problèmes de détection de changement dans la série Angular

亚连
Libérer: 2018-06-04 14:39:39
original
1490 Les gens l'ont consulté

Cet article présente principalement l'explication détaillée de la détection de changement dans la série Angular. Maintenant, je la partage avec vous et la donne comme référence.

Vue d'ensemble

En termes simples, la détection des changements est ce qu'Angular utilise pour détecter si la valeur liée entre la vue et le modèle a modifié. Lorsqu'il est détecté que la valeur liée dans le modèle change, elle est synchronisée avec la vue. Au contraire, lorsqu'il est détecté que la valeur liée dans la vue change, la fonction de liaison correspondante est rappelée.

Dans quelles circonstances la détection de changement sera-t-elle provoquée ?

Pour résumer, il existe principalement les situations suivantes qui peuvent également modifier les données :

  1. Opérations de saisie utilisateur, telles que clics, soumissions, etc.

  2. Demander des données du serveur (XHR)

  3. Événements chronométrés, tels que setTimeout, setInterval

Les trois situations ci-dessus ont toutes une chose en commun, c'est-à-dire que les événements qui provoquent la modification de la valeur de liaison se produisent de manière asynchrone. Si ces événements asynchrones peuvent avertir le framework Angular lorsqu'ils se produisent, alors le framework Angular peut détecter les changements dans le temps.

Le côté gauche représente le code à exécuter. La pile ici représente la pile en cours d'exécution de Javascript, tandis que webApi est une API Javascript fournie par le navigateur. en Javascript Queue, étant donné que Javascript est monothread, les tâches asynchrones sont exécutées dans une file d'attente de tâches.

Plus précisément, le mécanisme de fonctionnement de l'exécution asynchrone est le suivant :

  1. Toutes les tâches synchrones sont exécutées sur le thread principal, formant une pile de contexte d'exécution.

  2. En plus du fil de discussion principal, il existe également une "file d'attente des tâches". Tant que la tâche asynchrone a des résultats en cours d'exécution, un événement est placé dans la « file d'attente des tâches ».

  3. Une fois que toutes les tâches de synchronisation de la « pile d'exécution » ont été exécutées, le système lira la « file d'attente des tâches » pour voir quels événements s'y trouvent. Les tâches asynchrones correspondantes mettent fin à l'état d'attente, entrent dans la pile d'exécution et démarrent l'exécution.

  4. Le fil principal ne cesse de répéter la troisième étape ci-dessus.

Lorsque le code ci-dessus est exécuté en Javascript, func1 entre d'abord dans la pile en cours d'exécution, après l'exécution de func1, setTimeout entre dans la pile en cours d'exécution. Lors de l'exécution de setTimeout, la fonction de rappel cb est. ajouté à la file d'attente des tâches. Ensuite, setTimeout sort de la pile, puis exécute la fonction func2. Lorsque la fonction func2 est exécutée, la pile en cours d'exécution est vide, puis cb dans la file d'attente des tâches entre dans la pile en cours d'exécution et est exécuté. On peut voir que la tâche asynchrone entrera d'abord dans la file d'attente des tâches lorsque toutes les tâches synchrones de la pile en cours d'exécution auront été exécutées, la tâche asynchrone entrera dans la pile en cours d'exécution et sera exécutée. Si certaines fonctions de hook peuvent être fournies avant et après l'exécution de ces tâches asynchrones, Angular peut connaître l'exécution des tâches asynchrones grâce à ces fonctions de hook.

angular2 Recevoir une notification de modification

Alors la question est : comment angulaire2 sait-il que les données ont changé ? Comment savoir où le DOM doit être modifié et comment modifier le DOM dans la plage la plus petite possible ? Oui, modifiez le DOM le plus petit possible, car manipuler le DOM est un luxe en termes de performances.

Dans AngularJS, il est déclenché par le code $scope.$apply() ou $scope.$digest, et Angular est connecté à ZoneJS, qui surveille tous les événements asynchrones d'Angular.

Comment fait ZoneJS ?

En fait, Zone a quelque chose appelé Monkey Patching. Lorsque Zone.js est en cours d'exécution, une couche de packaging proxy sera créée pour ces événements asynchrones, c'est-à-dire qu'après l'exécution de Zone.js, lorsque des événements asynchrones de navigateur tels que setTimeout et addEventListener sont appelés, les méthodes natives ne sont plus utilisées. appelé, mais sont appelés méthode proxy patchée par Monkey. Les fonctions de hook sont configurées dans l'agent. Grâce à ces fonctions de hook, vous pouvez facilement entrer dans le contexte d'exécution de tâches asynchrones

//以下是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;
Copier après la connexion

<. 🎜> changements Le processus de détection

Le cœur d'Angular est la composantisation, et l'imbrication des composants finira par former une arborescence de composants. La détection de changement d'Angular peut être divisée en composants. Chaque composant correspond à un changeDetector Nous pouvons obtenir le changeDetector par injection de dépendances dans le composant. Nos multiples Composants sont organisés dans une structure arborescente. Puisqu'un Composant correspond à un changeDetector, les changeDetectors sont également organisés dans une structure arborescente.

De plus, le flux de données d'Angular vient du haut, un-. le chemin passe du composant parent au composant enfant. Le flux de données unidirectionnel garantit une détection efficace et prévisible des changements. Bien qu'après avoir vérifié le composant parent, le composant enfant puisse modifier les données du composant parent de sorte que le composant parent doive être à nouveau vérifié. Ce n'est pas une manière recommandée de gérer les données. En mode développement, Angular effectuera une vérification secondaire. Si la situation ci-dessus se produit, la vérification secondaire signalera une erreur : Expression modifiée après avoir été vérifiée. Dans un environnement de production, la vérification sale n’est effectuée qu’une seule fois.

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

变化检测策略

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

defalut 策略

main.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"></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;);
 } 
}
Copier après la connexion

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;
}
Copier après la connexion

上面代码中, 当点击第一个按钮改变明星属性时,依次对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
 ){}
Copier après la connexion

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

  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;;
 }
}
Copier après la connexion

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

![图片描述][3]

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

ngOnInit() {
  setInterval(() => {
   this.star.lastName = &#39;xjl&#39;;
   this.changeRef.markForCheck();
  }, 1000);
 }
Copier après la connexion

输入属性为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;);
 } 
}
Copier après la connexion

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

ngOnInit() {
  this.addCount.subscribe(() => {
   this.count++;
   this.changeRef.markForCheck();
  })
Copier après la connexion

另外一种是使用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
})
Copier après la connexion

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

相关文章:

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

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

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal