Maison > Article > interface Web > Une brève analyse du mécanisme de mise à jour du DOM dans la détection des changements angulaires
La détection des changements est une partie importante de Angular, qui consiste à maintenir la synchronisation entre le modèle et la vue. Dans le processus de développement quotidien, nous n'avons pas besoin de connaître la détection des changements, car Angular nous aide à terminer cette partie du travail, permettant aux développeurs de se concentrer davantage sur la mise en œuvre commerciale, améliorant ainsi l'efficacité du développement et l'expérience de développement. Mais si vous souhaitez utiliser le framework en profondeur, ou si vous souhaitez écrire du code hautes performances au lieu de simplement implémenter des fonctions, vous devez comprendre la détection des changements. Cela peut nous aider à mieux comprendre le framework, à déboguer les erreurs, à améliorer les performances, etc. [Recommandation de didacticiel connexe : "Tutoriel angulaire"]
Regardons d'abord un petit exemple.
Lorsque nous cliquons sur le bouton, l'attribut de nom est modifié et le DOM est automatiquement mis à jour avec la nouvelle valeur de nom.
Maintenant, il y a une question. Si je change la valeur de name puis que je produis le innerText dans le DOM, quelle sera cette valeur ?
import { Component, ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent { name = 'Empty'; @ViewChild('textContainer') textContainer: ElementRef; normalClick(): void { this.name = 'Hello Angular'; console.log(this.textContainer.nativeElement.innerText); } }
Avez-vous répondu correctement ?
Alors, que se passe-t-il exactement dans ces deux morceaux de code ?
Si nous utilisons JS natif pour écrire ce code, alors la vue ne changera certainement pas après avoir cliqué sur le bouton, mais la vue change dans Angular, alors pourquoi met-elle automatiquement à jour la vue ? Ceci est indissociable d'une bibliothèque appelée zone.js. Pour faire simple, il effectue un traitement des événements où les valeurs changent. Cela sera expliqué en détail dans les sections suivantes. Il suffit de le savoir pour l'instant.
Si je ne veux pas que cette bibliothèque effectue ce traitement, Angular nous fournit également un moyen de désactiver zone.js.
Vous pouvez désactiver zone.js dans main.ts.
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: 'noop' }) .catch(err => console.error(err));
Lorsque nous désactivons zone.js, la vue n'est pas mise à jour. Accédez au code source pour trouver le code pertinent pour la mise à jour de la vue.
*/ class ApplicationRef { /** @internal */ constructor(_zone, _injector, _exceptionHandler, _initStatus) { this._zone = _zone; this._injector = _injector; this._exceptionHandler = _exceptionHandler; this._initStatus = _initStatus; /** @internal */ this._bootstrapListeners = []; this._views = []; this._runningTick = false; this._stable = true; this._destroyed = false; this._destroyListeners = []; /** * Get a list of component types registered to this application. * This list is populated even before the component is created. */ this.componentTypes = []; /** * Get a list of components registered to this application. */ this.components = []; this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); } }); ... } /** * Invoke this method to explicitly process change detection and its side-effects. * * In development mode, `tick()` also performs a second change detection cycle to ensure that no * further changes are detected. If additional changes are picked up during this second cycle, * bindings in the app have side-effects that cannot be resolved in a single change detection * pass. * In this case, Angular throws an error, since an Angular application can only have one change * detection pass during which all change detection must complete. */ tick() { NG_DEV_MODE && this.warnIfDestroyed(); if (this._runningTick) { const errorMessage = (typeof ngDevMode === 'undefined' || ngDevMode) ? 'ApplicationRef.tick is called recursively' : ''; throw new RuntimeError(101 /* RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK */, errorMessage); } try { this._runningTick = true; for (let view of this._views) { view.detectChanges(); } if (typeof ngDevMode === 'undefined' || ngDevMode) { for (let view of this._views) { view.checkNoChanges(); } } } catch (e) { // Attention: Don't rethrow as it could cancel subscriptions to Observables! this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e)); } finally { this._runningTick = false; } } }
Interprétation large, ce ApplicationRef est une instance de l'application Angular entière. Dans le constructeur, onMicrotaskEmpty de zone (bibliothèque de zones) (d'après le nom, c'est un sujet qui efface les micro-tâches). souscrit une fois. Dans l'abonnement, tick() est appelé, alors que fait-on dans le tick ?
Réflexion :J'ai dit la dernière fois qu'il valait mieux ne pas s'abonner au constructeur. Pourquoi est-ce si irrégulier ici ? Bien sûr que non. La dernière fois, nous avons parlé des composants angulaires qui doivent être placés dans
constructoret de ceux qui doivent être placés dans ngOnInit. Mais ici, ApplicationRef est un service, donc le code d'initialisation ne peut être placé que dans constructor. Dans la fonction tick, s'il s'avère que la fonction tick est en cours d'exécution, une exception sera levée car il s'agit d'une instance de l'application entière et ne peut pas être appelée de manière récursive. Ensuite, toutes les vues sont parcourues, puis chaque vue exécute
detectChanges(), ce qui signifie que la détection des changements est exécutée. Qu'est-ce que la détection des changements sera expliqué en détail plus tard. Ensuite, s'il s'agit de devMode, parcourez à nouveau toutes les vues et chaque vue exécute checkNoChanges() pour vérifier s'il y a des changements. S'il y a des changements, une erreur sera générée (Je parlerai de ce problème en détail plus tard. , sautez-le pour l'instant) . D'accord, maintenant je sais comment le mettre à jour, c'est-à-dire appeler la méthode
tick de ApplicationRef. import { Component, ViewChild, ElementRef, ApplicationRef } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
name = 'Empty';
@ViewChild('textContainer') textContainer: ElementRef = {} as any;
constructor(private app: ApplicationRef){}
normalClick(): void {
this.name = 'Hello Angular';
console.log(this.textContainer.nativeElement.innerText);
this.app.tick();
}
}
Effectivement, la vue peut être mise à jour normalement.
Revoyons-le brièvement. La mise à jour du DOM repose sur le déclenchement de
tick()zone.js aide les développeurs à éviter de déclencher cette opération manuellement. D'accord, vous pouvez maintenant activer zone.js. Alors, qu’est-ce que la détection de changement ? Continuez à attendre avec impatience le prochain article.
Pour plus de connaissances liées à la programmation, veuillez visiter :
Enseignement de la programmationCe 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!