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.
Durch diesen Artikel können Sie dabei helfen, dieses Wissen zu erlangen:
Sehen wir uns zunächst einen davon an die beliebteste Simply-Demo
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.
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 :
Angular stellt uns auch eine Methode zum Deaktivieren von zone.js zur Verfügung .
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
Dieses Mal haben wir diese Methode manuell im Code aufgerufen.
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.Vielleicht 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?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.
ChangeDetectionStrategy.Default
),父组件的变更检测发生时,子组件也会触发变更检测。
(CD
即为 changeDetection
)
每次变更检测时,都会比较新旧状态,如果两次变更检测(开发环境下)的结果不一致就会报错,例如:
Expression has changed after it was checked
这也就解释了为什么在子组件中更改了父组件的值会报错。
但是!在前面的两个例子中我们都在子组件中更改了父组件的值,只有第一个报错,第二个是可以正常更新的,如果你也同样很疑惑这中间真正的差异点在哪里,那么接着往下阅读吧~
先上结论:
更新所有子组件的绑定属性
调用所有子组件的 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 = 'child'
好了,到这里我们已经明白了这种错误发生的真正原因,但是我还是要提醒一下,这种错误只会在开发环境下触发,生产环境下会调用 enableProdMode()
,变更检测次数会从 2 降到 1,这部分在 Angular 源码当中也有描述。
当然你不能因为这个 bug 就强制在开发环境下使用生产模式...
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~🎜export declare abstract class ChangeDetectorRef { abstract checkNoChanges(): void; abstract detach(): void; abstract detectChanges(): void; abstract markForCheck(): void; abstract reattach(): void; }
enableProdMode()< in der Produktion aufgerufen wird Umgebung. /code>, 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 .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 erfahren Sie mehr über die Änderungserkennung in Angular" 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 为开发者提供的一便捷操作方式。
用动图来表示就是:查看链接
知名的 Angular 开源组件库 ng-zorro 就使用了大量的 OnPush 策略,这也是 Angular 性能优化的方法之一。
Angular 给每个组件都关联了一份组件视图,通过 ChangeDetectorRef
可以拿到相关联的视图,在定义中我们可以看到:
export declare abstract class ChangeDetectorRef { abstract checkNoChanges(): void; abstract detach(): void; abstract detectChanges(): void; abstract markForCheck(): void; abstract reattach(): void; }
观察下面的动图,被 detached
的组件将不会被检查变更。
而 reattach
则可以让被 detached
的组件重新可以被检测到变更。
reattach
只会重新启用对当前组件的变更检测,但是如果父组件没有启动变更检测,那么 reattach
并不会起作用,而 markForCheck
可以很好地解决这个问题。
这一点在 ng-zorro 的源码中可以了解一二。
例如在 nz-anchor 组件中更改 nz-anchor-link 组件的 active 属性时,由于本身 ChangeDetectionStrategy
为 OnPush
,那么就需要激活 markForCheck 来重新启用检测。具体写法可以查看 github 中的源代码。
用动图来展示则是这样,注意观察设置了 MFC 的前后变化
这个方法如同字面意思一样很好理解,就是触发一次变更检测啦,还记得本文中的第一个例子吗,我们不手动触发 tick()
,而是触发 detechtChanges()
也是可以达到效果的。
到这里,我相信大家已经基本弄明白了 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!