Was ist der Unterschied zwischen den folgenden zwei beobachtbaren Abbildungen?
(wenn dir etwas im folgenden Code seltsam erscheint: es stammt von einem Hobby-Hobby-Projekt; ich lerne immer noch RxJS)
Ich habe eine Komponente mit einem Getter und einem Konstruktor. Beide lesen Informationen aus dem ngrx-Store der App und extrahieren eine Zeichenfolge ( name
).
Der einzige Unterschied zwischen dem Getter und dem Konstruktor: Der Getter wird im HTML verwendet und die Observable, die zurückgegeben wird, wird über ein async
pipe gesendet, während das beobachtbare Mapping im Konstruktor beendet ist durch ein Abonnement mit subscribe
. Ich erwarte, dass beide so oft feuern, wie ein neuer Wert für name
verfügbar wird.
Aber stattdessen funktioniert nur der Getter so und stellt das async
pipe im HTML zur Verfügung, wo es mit neuen Werten des Namens verwendet wird ( console.log('A')
wird für jede Namensänderung aufgerufen). Der Rückruf von subscribe
wird nur einmal aufgerufen: console.log('B')
und console.log('B!')
werden beide genau einmal und nie wieder aufgerufen.
Wie kann dieser Unterschied im Verhalten erklärt werden?
Snippet von meiner Komponente:
%Vor% Zusätzliche Information: Wenn ich ngOnInit
hinzufüge, wird dieser Lebenszyklus-Hook während des gesamten Tests genau einmal aufgerufen. Wenn ich das Abonnement vom Konstruktor in den Lebenszyklus-Hook ngOnInit
verschiebe, funktioniert es nicht besser als innerhalb des Konstruktors. Genau das gleiche (unerwartete) Verhalten. Gleiches gilt für ngAfterViewInit
und weitere Lebenszyklus-Haken.
Ausgabe für den Namen ändert 'some-name' -> 'some-other-name' -> 'some-third-name' -> 'some-fourth-name' -> 'some-fifth-name'
:
[UPDATE] wie von Pace in ihrem Kommentar vorgeschlagen, fügte ich Getter Anrufprotokolle
hinzu [UPDATE] do
s wurde hinzugefügt wie von Pace vorgeschlagen
Beispielinhalt der Ausgabe, die von console.log
s in do
s:
Scheint so, als ob das Abonnement subscribe
nach dem ersten Aufruf verloren geht. Aber warum?
Das von ngrx.select()
zurückgegebene Observable wird nur ausgelöst, wenn sich die Daten im Speicher geändert haben.
Wenn das Observable ausgelöst werden soll, wenn sich initialName
ändert, würde ich empfehlen, initialName in ein RXJS Subject
zu konvertieren und combineLatest
zu verwenden:
Sie sollten niemals einen solchen Getter verwenden. Gibt nicht ein Observable von einem Getter zurück.
Angular wird sich immer wieder abmelden / abonnieren, wenn ein Änderung Erkennungszyklus passiert (was viel passiert).
Für jetzt schreibe ich "CD" für "Change Detection"
Einfache Demo davon:
Nehmen Sie eine wirklich einfache Komponente:
%Vor% Sie werden getting a new value
in Ihrer Konsole sehen, und jedes Mal, wenn Sie auf die Schaltfläche "Click to trigger change detection" klicken, bei der ein (click)
Event registriert ist, wird ein neuer CD-Zyklus ausgelöst.
Und wenn Sie auf diese Schaltfläche klicken, sehen Sie, dass Sie zweimal getting a new value
erhalten.
(Zweimal, weil wir nicht im Produktionsmodus sind und Angular 2 CD-Zyklen ausführt, um sicherzustellen, dass sich die Variable zwischen der ersten und zweiten Änderungserkennung nicht geändert hat, was zu Problemen führen kann, aber das ist eine andere Geschichte).
Der Punkt von einem Observablen ist, dass er für eine lange Zeit offen bleibt und Sie sollten davon profitieren. Um den vorherigen Code umzuformatieren, damit das Abonnement geöffnet bleibt und um das erneute Abmelden / Abonnieren zu vermeiden, können wir den Getter einfach loswerden und eine öffentliche Variable deklarieren (zugänglich durch die Vorlage):
%Vor% Und nun, egal wie oft Sie auf die Schaltfläche klicken, sehen Sie ein und nur ein getting a new value
(bis das Observable natürlich einen neuen Wert ausgibt), aber die Änderungserkennung wird nicht triggert ein neues Abonnement.
Hier ist eine Live-Demo auf Stackblitz , damit du herumspielen und die console.log
happening = sehen kannst)
Ссылка
BEARBEITEN:
A getter
ist eine Funktion, und daher muss Angular sie auf jeder CD aufrufen, um zu prüfen, ob ein neuer Wert von ihr kommt, der in der Ansicht aktualisiert werden soll. Das kostet viel, aber es ist das Prinzip und die "Magie" des Rahmens. Aus diesem Grund sollten Sie es vermeiden, intensive CPU-Aufgaben in einer Funktion auszuführen, die auf jeder CD ausgelöst werden könnte. Wenn es eine reine Funktion ist (gleiche Eingabe gleiche Ausgabe UND keine Nebeneffekte), verwenden Sie eine Pipe, da sie standardmäßig als "rein" betrachtet werden und die Ergebnisse zwischenspeichern. Für dieselben Argumente werden sie die Funktion in der Pipe nur einmal ausführen, das Ergebnis zwischenspeichern und dann das Ergebnis sofort zurückgeben, ohne die Funktion erneut auszuführen.
Tags und Links angular rxjs ngrx observable angular2-observables