Heim > Artikel > Web-Frontend > Detaillierte Interpretation von Problemen bei der Änderungserkennung in der Angular-Reihe
Dieser Artikel stellt hauptsächlich die detaillierte Erklärung der Änderungserkennung in der Angular-Reihe vor. Jetzt teile ich ihn mit Ihnen und gebe ihn als Referenz.
Übersicht
Einfach ausgedrückt ist die Änderungserkennung das, was Angular verwendet, um zu erkennen, ob die Wertgrenze zwischen der Ansicht und dem Modell vorhanden ist Ändern: Wenn festgestellt wird, dass sich der im Modell gebundene Wert ändert, wird er mit der Ansicht synchronisiert. Wenn hingegen festgestellt wird, dass sich der in der Ansicht gebundene Wert ändert, wird die entsprechende Bindungsfunktion zurückgerufen.
Unter welchen Umständen wird eine Änderungserkennung verursacht?
Zusammenfassend lässt sich sagen, dass es hauptsächlich die folgenden Situationen gibt, die ebenfalls zu einer Änderung der Daten führen können:
Benutzereingabevorgänge, wie z Klicken, Senden usw.
Serverdaten anfordern (XHR)
Zeitgesteuerte Ereignisse wie setTimeout, setInterval
Die oben genannten drei Situationen haben alle eines gemeinsam: Die Ereignisse, die dazu führen, dass sich der Bindungswert ändert, treten asynchron auf. Wenn diese asynchronen Ereignisse das Angular-Framework bei ihrem Auftreten benachrichtigen können, kann das Angular-Framework Änderungen rechtzeitig erkennen.
Die linke Seite stellt den auszuführenden Code dar, und webApi ist eine vom Browser bereitgestellte Javascript-API In der Javascript-Warteschlange werden asynchrone Aufgaben in einer Aufgabenwarteschlange ausgeführt, da Javascript Single-Threaded ist.
Im Einzelnen ist der Betriebsmechanismus der asynchronen Ausführung wie folgt:
Alle synchronen Aufgaben werden im Hauptthread ausgeführt und bilden einen Ausführungskontextstapel.
Neben dem Hauptthread gibt es auch eine „Aufgabenwarteschlange“. Solange die asynchrone Aufgabe laufende Ergebnisse hat, wird ein Ereignis in die „Aufgabenwarteschlange“ gestellt.
Sobald alle Synchronisierungsaufgaben im „Ausführungsstapel“ ausgeführt wurden, liest das System die „Aufgabenwarteschlange“, um zu sehen, welche Ereignisse darin enthalten sind. Diese entsprechenden asynchronen Aufgaben beenden den Wartezustand, betreten den Ausführungsstapel und beginnen mit der Ausführung.
Der Hauptthread wiederholt immer wieder den dritten Schritt oben.
Wenn der obige Code in Javascript ausgeführt wird, tritt zuerst func1 in den laufenden Stapel ein. Nachdem func1 ausgeführt wurde, tritt setTimeout in den laufenden Stapel ein. Während der Ausführung von setTimeout ist die Rückruffunktion cb Dann wird setTimeout aus dem Stapel hinzugefügt und dann die Funktion func2 ausgeführt. Wenn die Funktion func2 ausgeführt wird, ist der laufende Stapel leer, und dann wird cb in der Aufgabenwarteschlange in den laufenden Stapel aufgenommen und ausgeführt. Es ist ersichtlich, dass die asynchrone Aufgabe zuerst in die Aufgabenwarteschlange gelangt. Wenn alle synchronen Aufgaben im laufenden Stapel ausgeführt wurden, gelangt die asynchrone Aufgabe in den laufenden Stapel und wird ausgeführt. Wenn vor und nach der Ausführung dieser asynchronen Aufgaben einige Hook-Funktionen bereitgestellt werden können, kann Angular über diese Hook-Funktionen die Ausführung der asynchronen Aufgaben erkennen.
Angular2 Änderungsbenachrichtigung erhalten
Dann stellt sich die Frage: Woher weiß Angular2, dass sich die Daten geändert haben? Woher wissen Sie, wo das DOM geändert werden muss und wie Sie das DOM im kleinstmöglichen Bereich ändern können? Ja, ändern Sie das DOM so klein wie möglich, da die Manipulation des DOM ein Luxus für die Leistung ist.
In AngularJS wird es durch den Code $scope.$apply() oder $scope.$digest ausgelöst, und Angular ist mit ZoneJS verbunden, das alle asynchronen Ereignisse von Angular überwacht.
Wie macht ZoneJS das?
Eigentlich gibt es in Zone etwas namens Monkey Patching. Wenn Zone.js ausgeführt wird, wird eine Ebene der Proxy-Verpackung für diese asynchronen Ereignisse erstellt. Das heißt, wenn asynchrone Browserereignisse wie setTimeout und addEventListener aufgerufen werden, sind die nativen Methoden nicht mehr vorhanden aufgerufen, aber von Monkey gepatchte Proxy-Methode aufgerufen. Hook-Funktionen werden im Agenten eingerichtet, Sie können den Kontext der asynchronen Aufgabenausführung einfach
ändern ErkennungsprozessDer Kern von Angular ist die Komponentisierung, und die Verschachtelung von Komponenten bildet schließlich einen Komponentenbaum. Die Änderungserkennung von Angular kann in Komponenten unterteilt werden. Jede Komponente entspricht einem ChangeDetector. Wir können den ChangeDetector durch Abhängigkeitsinjektion in der Komponente erhalten. Unsere mehreren Komponenten sind in einer Baumstruktur organisiert. Da eine Komponente einem ChangeDetector entspricht, sind auch die ChangeDetectors in einer Baumstruktur organisiert.
相比之下,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 = 'change detection'; title: string = 'default 策略'; star: Star = new Star('周', '杰伦'); changeStar() { this.star.firstName = '吴'; this.star.lastName = '彦祖'; } changeStarObject() { this.star = new Star('刘', '德华'); } }
movie.component.ts
@Component({ selector: 'movie', styles: ['p {border: 1px solid black}'], 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 ){}
变化检测对象提供的方法有以下几种:
markForCheck() - 在组件的 metadata 中如果设置了 changeDetection:ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法, 该方法的意思是在变化监测时必须检测该组件。
detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。
reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测
detectChanges() - 从该组件到各个子组件执行一次变化检测
OnPush策略下手动发起变化检测
组件中添加事件改变输入属性
在上面代码movie.component.ts中修改如下
@Component({ selector: 'movie', styles: ['p {border: 1px solid black}'], 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 = 'xjl'; } }
此时点击按钮切换名字时,star更改如下
![图片描述][3]
第二种就是上面讲到的使用变化检测对象中的 markForCheck()方法.
ngOnInit() { setInterval(() => { this.star.lastName = 'xjl'; this.changeRef.markForCheck(); }, 1000); }
输入属性为Observable
修改app.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" [addCount]="count"></movie>`, }) export class AppComponent implements OnInit{ slogan: string = 'change detection'; title: string = 'OnPush 策略'; star: Star = new Star('周', '杰伦'); count:Observable<any>; ngOnInit(){ this.count = Observable.timer(0, 1000) } changeStar() { this.star.firstName = '吴'; this.star.lastName = '彦祖'; } changeStarObject() { this.star = new Star('刘', '德华'); } }
此时,有两种方式让MovieComponent进入检测,一种是使用变化检测对象中的 markForCheck()方法.
ngOnInit() { this.addCount.subscribe(() => { this.count++; this.changeRef.markForCheck(); })
另外一种是使用async pipe 管道
@Component({ selector: 'movie', styles: ['p {border: 1px solid black}'], 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 })
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Das obige ist der detaillierte Inhalt vonDetaillierte Interpretation von Problemen bei der Änderungserkennung in der Angular-Reihe. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!