Heim >Web-Frontend >js-Tutorial >Die nächste Verbesserung der Winkelreaktivität

Die nächste Verbesserung der Winkelreaktivität

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2024-11-17 22:19:01271Durchsuche

The next improvement in Angular reactivity

Einführung

Seit den neuesten Versionen von Angular wurde innerhalb des Frameworks ein neues primitives Reaktivitätssystem entwickelt: Signale!

Heute stellen wir im Nachhinein fest, dass bestimmte Anwendungsfälle nicht abgedeckt wurden, und offensichtlich wird uns das Angular-Team, das sehr reaktiv ist, Helfer zur Verfügung stellen, um diese Anwendungsfälle abzudecken.

Was sind diese Anwendungsfälle? Welche Lösungen werden eingeführt und wie werden sie genutzt?

Prozess des Zurücksetzens eines Signals relativ zu einem anderen

Beginnen wir mit der Veranschaulichung dieses Problems.

Stellen wir uns vor, wir haben einen Obstkorb mit einer bestimmten Menge.
Die Menge wird durch eine Komponente verwaltet, die die Frucht eingibt.

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

Hier muss die Variable zurückgesetzt werden, wenn sich der Eingabepreis Obst ändert.

Eine einfache Lösung wäre die Verwendung eines Effekts

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

Der vorstehende Code ist eine schlechte Praxis. Warum ist das die große Frage?

Wir müssen die Option signalWrites auf true setzen, um die Signalmenge festzulegen. Dies liegt an einer Fehlinterpretation des gegebenen Problems.
In unserem Fall möchten wir zwei Variablen synchronisieren, die in unserer Materialisierung desynchronisiert sind

Der Zähler ist nicht unabhängig von der Frucht, die unsere Ausgangsquelle ist. In Wirklichkeit haben wir hier einen Komponentenzustand, dessen ursprüngliche Quelle die Frucht ist und der Rest ein Derivat der Frucht ist.

Materialisieren Sie das Problem wie folgt

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}

Diese Materialisierung verknüpft die Frucht stark mit ihrer Menge.
Sobald sich also die Frucht ändert, wird die berechnete Variable FruitState automatisch neu berechnet. Diese Neuberechnung gibt ein Objekt mit der Mengeneigenschaft zurück, bei dem es sich um ein auf 1 initialisiertes Signal handelt.

Durch die Rückgabe eines Signals kann die Variable bei einem Klick erhöht und einfach zurückgesetzt werden, wenn sich die Frucht ändert.

Es ist ein relativ einfaches Muster einzurichten, aber können wir es nicht vereinfachen?

Die LinkedSignal-Funktion zur Rettung.

Mit der Einführung von Angular 19 eine neue Funktion zur Berechnung abgeleiteter Signale.

Bisher hatten wir die berechnete Funktion, aber diese Funktion gibt ein Signal und kein WrittableSignal zurück, was in unserem vorherigen Anwendungsfall für die Mengenvariable praktisch gewesen wäre.

Hier kommt LinkedSignal ins Spiel. LinkedSignal ermöglicht Ihnen, wie der Name schon sagt, die starke Verknüpfung zweier Signale miteinander.

Wenn wir zu unserem vorherigen Fall zurückkehren, würde uns diese Funktion ermöglichen, den Code wie folgt zu vereinfachen:

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

Die LinkedInedSignal-Funktion ist wie folgt definiert:

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

In der ersten Definition, der „abgekürzten“ Definition, akzeptiert die Funktion „linkedSignal“ eine Berechnungsfunktion als Parameter und ein Konfigurationsobjekt.

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

Da in diesem vorherigen Beispiel die Berechnungsfunktion vom Mengensignal abhängt, wird die Berechnungsfunktion bei einer Mengenänderung neu bewertet.

In der zweiten Definition akzeptiert die LinkedInFunction-Methode ein Objekt als Parameter mit drei Eigenschaften

  • die Quelle: das Signal, auf dem die Berechnungsfunktion ihre Neubewertung basiert
  • die Berechnungsfunktion
  • ein Parameterobjekt

Im Gegensatz zur „abgekürzten“ Berechnungsfunktion verwendet die Berechnungsfunktion hier als Parameter den Wert der Quelle und einen „Präzedenzfall“.

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

Die neue Ressourcen-API

Angular 19 wird eine neue API für den einfachen Datenabruf und den Abfragestatus (ausstehend usw.), Daten und Fehler einführen.

Für diejenigen, die mit dem Framework ein wenig vertraut sind: Diese neue API funktioniert ein bisschen wie der useRessource-Hook.

Schauen wir uns ein Beispiel an:

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}

Über dieses Code-Snippet gibt es einiges zu wissen

In diesem Snippet-Code sind mehrere Dinge zu beachten:

  • Standardmäßig akzeptiert der Loader ein Versprechen als Parameter
  • Der Typ „fruitRessource“ ist eine WrittableRessource, die es uns ermöglicht, die Daten bei Bedarf lokal zu ändern
  • Die Anfrage nach Fruchtdetails wird direkt an den Server gesendet, wenn die Ressource fuitResource erstellt wird
  • this.fruitId() wird im Loader nicht verfolgt, daher wird keine neue Anfrage gesendet, wenn sich die FruitId ändert
  • WrittableRessource verfügt über eine Aktualisierungsmethode, um die Daten zu aktualisieren

Der folgende Effekt druckt diesen Wert

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

Wie oben erläutert, wird das FruitId-Signal standardmäßig nicht verfolgt.

Wie starten Sie also die HTTP-Anfrage jedes Mal neu, wenn sich der Wert dieses Signals ändert, aber wie brechen Sie auch die vorherige Anfrage ab, falls sich der Wert des FruitId-Signals ändert, die Antwort auf die vorherige Anfrage jedoch nicht ankommen?

Die Ressourcenfunktion benötigt eine weitere Eigenschaft namens „request“.

Diese Eigenschaft nimmt als Wert eine Funktion an, die von Signalen abhängt und deren Wert zurückgibt.

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

Wie im obigen Code gezeigt, benötigt die Loader-Funktion zwei Parameter

  • der Anfragewert: hier der Wert des FruitId-Signals
  • Der Wert der Signaleigenschaft des abortController-Objekts, das zum Abbrechen einer http-Anfrage verwendet wird.

Wenn sich also der Wert des FruitId-Signals während einer httpRequest ändert, die die Details einer Frucht abruft, wird die Anfrage abgebrochen, um eine neue Anfrage zu starten.

Schließlich hat Angular auch über die Möglichkeit nachgedacht, diese neue API mit RxJs zu koppeln, sodass wir von der Leistungsfähigkeit der Rx-Operatoren profitieren können.

Interporabilität wird mithilfe der rxResource-Funktion erreicht, die genauso definiert ist wie die Ressourcenfunktion.
Der einzige Unterschied besteht im Rückgabetyp der Loader-Eigenschaft, die ein Observable
zurückgibt

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

Hier ist es nicht notwendig, das abortSignal zu haben, das Abbrechen der vorherigen Anfrage, wenn sich der Wert des Signals FruitId ändert, ist implizit in der Funktion rxResource und das Verhalten ist das gleiche wie beim switchMap-Operator.

Das obige ist der detaillierte Inhalt vonDie nächste Verbesserung der Winkelreaktivität. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn