Heim  >  Artikel  >  Web-Frontend  >  Hier erfahren Sie mehr über die Änderungserkennung in Angular

Hier erfahren Sie mehr über die Änderungserkennung in Angular

青灯夜游
青灯夜游nach vorne
2021-08-11 10:48:562338Durchsuche

In diesem Artikel erfahren Sie mehr über die Änderungserkennung in Angular und stellen Ihnen den DOM-Aktualisierungsmechanismus von Angular vor, welche Probleme die Änderungserkennung lösen kann usw.

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Durch diesen Artikel können Sie dabei helfen, dieses Wissen zu erlangen:

  • Wie erfolgt das DOM-Update von Angular? [Verwandte Tutorial-Empfehlung: „Angular-Tutorial“]
  • Welche Probleme löst die Änderungserkennung?
  • Verstehen Sie die Änderungserkennung besser, machen Sie sich mit ihrer Definition im Angular-Quellcode und ihrer Verwendung in ng-zorro vertraut
  • Und einige persönliche Erkenntnisse

1 Angulars DOM-Aktualisierungsmechanismus

Sehen wir uns zunächst einen davon an die beliebteste Simply-Demo

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Wenn wir auf die Schaltfläche klicken, ändern wir das Namensattribut der Komponente und der geänderte Wert wird sofort auch im DOM angezeigt, was ein wenig „magisch“ erscheint.

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Wenn der innterText im realen DOM unmittelbar nach der Elementänderungsanweisung gedruckt wird, aber festgestellt wird, dass es sich immer noch um den alten Wert handelt, sich der Wert in der Ansicht jedoch offensichtlich geändert hat, was genau ist in diesen beiden Teilen passiert? von Code? Wenn Sie sich darüber auch wundern, dann verraten Sie mir gemeinsam die Antwort.

Erinnern wir uns sorgfältig daran, was gerade passiert ist:

  • Klicken Sie auf die Schaltfläche

  • Der Wert ändert sich

Wenn Sie natives JS verwenden, um diesen Code zu schreiben, ändert sich die Ansicht nach dem Klicken auf die Schaltfläche definitiv nicht In Angular ändert sich die Ansicht. Wenn Sie über ein etwas tieferes Verständnis von Angular verfügen, kennen Sie eine Bibliothek namens „zone.js“. Wenn Sie genau hinschauen, werden Sie feststellen, dass „zone.js“ eine Verarbeitungsebene für alle Ereignisse durchführt, die Werte ändern können, z :

  • Ereignisse: Klick, Mouseover, Mouseout, Keyup, Keydown und andere Browserereignisse
  • Timer: setTimeout, setInterval
  • XHR: verschiedene Anfragen usw.

Angular stellt uns auch eine Methode zum Deaktivieren von zone.js zur Verfügung .

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Wenn wir nach dem Deaktivieren der Zone erneut auf die Schaltfläche klicken, wird die Ansicht nicht aktualisiert.

Mit Neugier haben wir den Schlüsselcode für die Ansichtsaktualisierung im Angular-Quellcode gefunden

Hier erfahren Sie mehr über die Änderungserkennung in Angular

Dieses Mal haben wir diese Methode manuell im Code aufgerufen.

Hier erfahren Sie mehr über die Änderungserkennung in Angular

7-Hier erfahren Sie mehr über die Änderungserkennung in Angular

Genau wie erwartet! Die Ansicht wird aktualisiert, und was noch überraschender ist, ist, dass auch der gedruckte innerText aktualisiert wird!

An diesem Punkt sind wir zu dem Schluss gekommen, dass die DOM-Aktualisierung auf dem Auslösen von tick() basiert und Zone.js Entwicklern dabei hilft, die Notwendigkeit einer manuellen Auslösung einzusparen.

Okay, nach einem kleinen Test werfen wir einen genaueren Blick darauf, was hinter dem Angular View-Update passiert.

2. Werfen wir einen Blick in die Geheimnisse der Änderungserkennung

1. Schauen wir uns zunächst einen solchen Fehler an, der in ngOnInit geändert wird Als Ergebnis ist eine Fehlermeldung aufgetreten, auf die jeder gestoßen sein muss. Aber das Schreiben auf diese Weise führt nicht immer zu einem Fehler. Wenn wir beispielsweise das Eingabeattribut der untergeordneten Komponente entfernen, Aktualisieren Sie es und stellen Sie fest, dass derselbe Code ausgeführt werden kann. Der Name der übergeordneten Komponente kann normal geändert werden.

emmm... In Gedanken versunken...

Hier erfahren Sie mehr über die Änderungserkennung in AngularVielleicht geht es Ihnen wie mir, als ich anfing, Angular zu lernen, nach diesem Problem im Stackoverflow suchte, einen Code kopierte, von dem ich nicht weiß, warum er funktioniert, und Dann habe ich es einfach eingefügt, und wenn ich wieder auf dieses Problem stoße, suche ich weiter, kopiere und füge es im Stackoverflow ein und so weiter ...

Mit der Zeit werden Sie, die sich mit verschiedenen CRUDs auskennen, zunehmend unzufrieden mit dieser Art von Stackoverflow-orientierter Programmierung. Sie beginnen, weiterhin nach Antworten auf Fragen in Communities, Dokumenten und Foren zu suchen, aber nachdem Sie deren Antworten gelesen haben In diesem Artikel weiß ich anscheinend nur, dass es etwas namens „Änderungserkennung“ gibt, aber ich kann nicht genau erklären, was diesen Fehler verursacht hat. Wenn Sie die gleiche Erfahrung machen wie ich, dann ... Lesen Sie weiter, um die Wahrheit herauszufinden !

2. Was ist die Änderungserkennung, über die ich schon lange gesprochen habe?

Wenn wir die Daten im Modell ändern, muss die Framework-Schicht wissen:

Wo im Modell sich geändert hat
  • Wo in der Ansicht aktualisiert werden muss
  • Jeder muss mit dem Virtual Dom in vertraut sein React. React vergleicht den neuen Status und den alten Status, um zu bestimmen, welcher Teil des Doms aktualisiert werden soll, anstatt den gesamten Dom zu aktualisieren. Dies ist auch dasselbe wie die Änderungserkennung in Angular.

Die gesamte Angular-Anwendung ist ein Komponentenbaum. Es ist unmöglich, dass Änderungen in einer Komponente die Aktualisierung aller Komponenten auslösen. Dies ist beispielsweise zu ineffizient und zeitaufwändig Der idealste Ansatz besteht darin, nur den Stil oder Text dieser Schaltfläche zu aktualisieren, anstatt die gesamte Anwendung zu aktualisieren. Dies ist der Zweck der Änderungserkennung.

Standardmäßig (ChangeDetectionStrategy.Default) löst die untergeordnete Komponente auch die Änderungserkennung aus, wenn die Änderungserkennung der übergeordneten Komponente erfolgt.

Hier erfahren Sie mehr über die Änderungserkennung in AngularChangeDetectionStrategy.Default),父组件的变更检测发生时,子组件也会触发变更检测。

Hier erfahren Sie mehr über die Änderungserkennung in Angular

(CD 即为 changeDetection )

每次变更检测时,都会比较新旧状态,如果两次变更检测(开发环境下)的结果不一致就会报错,例如:

Expression has changed after it was checked

这也就解释了为什么在子组件中更改了父组件的值会报错。

但是!在前面的两个例子中我们都在子组件中更改了父组件的值,只有第一个报错,第二个是可以正常更新的,如果你也同样很疑惑这中间真正的差异点在哪里,那么接着往下阅读吧~

3.问题的关键 — 检测顺序 Detection Sequence

先上结论:

  • 更新所有子组件的绑定属性

  • 调用所有子组件的 OnChanges,OnInit,DoCheck,AfterContentInit 生命周期钩子

  • 更新当前组件的 DOM

  • 子组件触发变更检测

  • 调用所有子组件的的 AfterViewInit 的生命周期钩子

这里我们不关注于太细的细节(不用好奇为什么是这样的顺序,只要记住 Angular 里就是这样设定的就可以了,如果有大佬想谈谈 Angular 在这部分的设计思想,欢迎在评论区留言探讨~)

第一个例子中,父组件 parent 给子组件 child 传入了输入属性 name,且子组件在 ngOnInit 中更新了父组件的 name 属性,也就是说这段代码**违背了检测顺序(**在顺序的第二步中操作了第一步)!

<p>{{ name }}<p>
<child [name]="name"></child>

而在第二个例子中,就算子组件在 ngOnInit 中也更新了父组件的 name 属性,但是由于父组件parent 中没有给子组件 child 绑定输入属性 name,不会出现与违背变更检测队列顺序的情况,所以就可以正常运行。

<p>{{ name }}<p>
<child></child>

这个时候再去看看 stackoverflow 上的高赞回答 是不是就清晰明了很多,按照上述的检测顺序,我们会发现只要父组件中对子组件做了属性绑定,不管是在 OnChanges,OnInit,DoCheck,AfterContentInit 和 AfterViewInit 中的任意一个声明周期钩子中执行下述代码都会报错。

this.parentCmpt.name = &#39;child&#39;

好了,到这里我们已经明白了这种错误发生的真正原因,但是我还是要提醒一下,这种错误只会在开发环境下触发,生产环境下会调用 enableProdMode() ,变更检测次数会从 2 降到 1,这部分在 Angular 源码当中也有描述。

Hier erfahren Sie mehr über die Änderungserkennung in Angular

当然你不能因为这个 bug 就强制在开发环境下使用生产模式...

4.大家常说的 ChangeDetectionStrategy.OnPush 又是什么?

ChangeDetectionStrategy 默认为 Default,也就是父组件的 CD 会触发子组件的 CD,但是很显然有些情况下我们可以自行判断出某些子组件在父组件 CD 时并不用触发,而 OnPush

( CD ist changeDetection )🎜🎜Jedes Mal, wenn eine Änderung erkannt wird, werden der alte und der neue Status verglichen. Wenn die Ergebnisse der beiden Änderungserkennungen (in der Entwicklungsumgebung) vorliegen. inkonsistent sind, wird ein Fehler gemeldet, zum Beispiel: 🎜🎜Ausdruck hat sich geändert, nachdem er überprüft wurde🎜🎜Dies erklärt auch, warum ein Fehler gemeldet wird, wenn der Wert der übergeordneten Komponente in der untergeordneten Komponente geändert wird Komponente. 🎜🎜Aber! In den beiden vorherigen Beispielen haben wir den Wert der übergeordneten Komponente in der untergeordneten Komponente geändert. Nur das erste hat einen Fehler gemeldet, und das zweite konnte normal aktualisiert werden. Wenn Sie auch über den tatsächlichen Unterschied zwischen ihnen verwirrt sind, lesen Sie on~🎜

🎜3. Der Schlüssel zum Problem – Erkennungssequenz🎜🎜🎜Lassen Sie uns zunächst schlussfolgern: 🎜
    🎜🎜Aktualisieren Sie die Bindungseigenschaften aller Unterkomponenten.🎜🎜🎜🎜Rufen Sie die OnChanges-, OnInit-, DoCheck- und AfterContentInit-Lebenszyklus-Hooks aller Unterkomponenten auf.🎜🎜🎜🎜Aktualisieren Sie das DOM der aktuellen Komponente 🎜 🎜🎜Rufen Sie den AfterViewInit-Lebenszyklus-Hook aller Unterkomponenten auf🎜🎜🎜🎜Wir konzentrieren uns hier nicht auf zu feine Details (wundern Sie sich nicht, warum es in dieser Reihenfolge ist, denken Sie nur daran, dass es so eingerichtet ist). Angular) Ja, wenn jemand in diesem Teil über die Designideen von Angular sprechen möchte, hinterlassen Sie bitte eine Nachricht im Kommentarbereich, um dies zu besprechen ~) 🎜🎜Im ersten Beispiel übergibt das übergeordnete Element der übergeordneten Komponente den Namen des Eingabeattributs an das untergeordnete Element Die untergeordnete Komponente und die untergeordnete Komponente Das Namensattribut der übergeordneten Komponente wird in ngOnInit aktualisiert, was bedeutet, dass dieser Code gegen die Erkennungssequenz verstößt (der erste Schritt wird im zweiten Schritt der Sequenz ausgeführt)! 🎜
    export declare abstract class ChangeDetectorRef {
        abstract checkNoChanges(): void;
        abstract detach(): void;
        abstract detectChanges(): void;
        abstract markForCheck(): void;
        abstract reattach(): void;
    }
    🎜Im zweiten Beispiel liegt kein Verstoß gegen die Änderungserkennung vor, selbst wenn die untergeordnete Komponente auch das Namensattribut der übergeordneten Komponente in ngOnInit aktualisiert, da das übergeordnete Element der übergeordneten Komponente den Eingabeattributnamen nicht an das untergeordnete Element der untergeordneten Komponente bindet . Warteschlangenreihenfolge, damit es normal ausgeführt werden kann. 🎜rrreee🎜Sehen Sie sich zu diesem Zeitpunkt hochgelobte Antworten auf Stackoverflow an Ist es nicht viel klarer? Gemäß der obigen Erkennungssequenz werden wir feststellen, dass, solange die übergeordnete Komponente über eine Eigenschaftsbindung an die untergeordnete Komponente verfügt, unabhängig davon, ob sie sich in einem der Lebenszyklus-Hooks zwischen OnChanges, OnInit, DoCheck, befindet. AfterContentInit und AfterViewInit Das Ausführen des folgenden Codes führt zu einem Fehler. 🎜rrreee🎜Okay, jetzt haben wir den wahren Grund verstanden, warum dieser Fehler auftritt, aber ich möchte Sie trotzdem daran erinnern, dass dieser Fehler nur in der Entwicklungsumgebung ausgelöst wird und enableProdMode(), die Anzahl der Änderungserkennungen sinkt von 2 auf 1, dieser Teil befindet sich in <a href="https://github.com/angular/angular/blob/6b79ab5abec8b5a4b43d563ce65f032990b3e3bc/packages/core/src/application_ref%20.ts#L553" target="_blank" ref="nofollow noopener noreferrer">Angular-Quellcode</a> wird ebenfalls beschrieben. 🎜🎜<img src="https://img.php.cn/upload/image/766/561/889/162864959954984Hier%20erfahren%20Sie%20mehr%20%C3%BCber%20die%20%C3%84nderungserkennung%20in%20Angular" title="162864959954984Hier erfahren Sie mehr über die Änderungserkennung in Angular" alt="Hier erfahren Sie mehr über die Änderungserkennung in Angular">🎜🎜Von Natürlich können Sie die Verwendung des Produktionsmodus in der Entwicklungsumgebung nur wegen dieses Fehlers nicht erzwingen...🎜<h3 data-id="heading-5">🎜4 Was ist ChangeDetectionStrategy.OnPush, über das alle oft reden? 🎜🎜🎜<code>ChangeDetectionStrategy ist standardmäßig auf Standard eingestellt, das heißt, die CD der übergeordneten Komponente löst die CD der untergeordneten Komponente aus, aber in einigen Fällen können wir natürlich selbst beurteilen, dass einige untergeordnete Komponenten nicht verwendet werden wenn die übergeordnete Komponente CD ausgelöst wird, während OnPush🎜

则是 Angular 为开发者提供的一便捷操作方式。

1Hier erfahren Sie mehr über die Änderungserkennung in Angular

用动图来表示就是:查看链接

1Hier erfahren Sie mehr über die Änderungserkennung in Angular

知名的 Angular 开源组件库 ng-zorro 就使用了大量的 OnPush 策略,这也是 Angular 性能优化的方法之一。

1Hier erfahren Sie mehr über die Änderungserkennung in Angular

三、再深入了解一些

Angular 给每个组件都关联了一份组件视图,通过 ChangeDetectorRef 可以拿到相关联的视图,在定义中我们可以看到:

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

1.detach 和 reattach

观察下面的动图,被 detached 的组件将不会被检查变更。

Hier erfahren Sie mehr über die Änderungserkennung in Angular

reattach 则可以让被 detached 的组件重新可以被检测到变更。

2.markForCheck

reattach 只会重新启用对当前组件的变更检测,但是如果父组件没有启动变更检测,那么 reattach 并不会起作用,而 markForCheck 可以很好地解决这个问题。

这一点在 ng-zorro 的源码中可以了解一二。

例如在 nz-anchor 组件中更改 nz-anchor-link 组件的 active 属性时,由于本身 ChangeDetectionStrategyOnPush ,那么就需要激活 markForCheck 来重新启用检测。具体写法可以查看 github 中的源代码。

用动图来展示则是这样,注意观察设置了 MFC 的前后变化

Hier erfahren Sie mehr über die Änderungserkennung in Angular

3.detectChanges

这个方法如同字面意思一样很好理解,就是触发一次变更检测啦,还记得本文中的第一个例子吗,我们不手动触发 tick() ,而是触发 detechtChanges() 也是可以达到效果的。

1Hier erfahren Sie mehr über die Änderungserkennung in Angular

Hier erfahren Sie mehr über die Änderungserkennung in Angular

四、最后

到这里,我相信大家已经基本弄明白了 Angular 变更检测,如果有任何疑问,欢迎在评论区交流讨论~

在撰写这篇文章时,笔者参(fu)考(zhi)了大量的社区文章和讨论,一方面是感慨如此重要的概念在 Angular 中文社区中却只有零星几篇相关介绍的文章,另一方面是看到了虽然国内 Angular 开发者虽然数量远少于 React 和 Vue,却依然非常热情的贡献自己的知识和见解来为 Angular 中文社区添砖加瓦,作为已使用 Angular 半年多的开发者,深深感受到 Google 的工程美学。

大而全且不失优雅,是笔者对 Angular 这款 Web 框架的最大感受,感谢开源社区中的各位开发者们~

对于文中描述错误的地方,还望大佬们批评斧正~

原文地址:https://juejin.cn/post/6844904079878012935

作者:LangWalker

更多编程相关知识,请访问:编程入门!!

Das obige ist der detaillierte Inhalt vonHier erfahren Sie mehr über die Änderungserkennung in Angular. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Vorheriger Artikel:So verbinden Sie JS-ArraysNächster Artikel:So verbinden Sie JS-Arrays