Detaillierte Erläuterung des Cache-Implementierungsprinzips von Vue-Berechnungen

Detaillierte Erläuterung des Cache-Implementierungsprinzips von Vue-Berechnungen

In diesem Artikel wird anhand des folgenden Beispiels der Vorgang der berechneten Initialisierung und Aktualisierung erläutert und gezeigt, wie die berechneten Eigenschaften zwischengespeichert und die Abhängigkeiten erfasst werden.

<div id="app">
  <span @click="change">{{sum}}</span>
</div>
<script src="./vue2.6.js"></script>
<Skript>
  neuer Vue({
    el: "#app",
    Daten() {
      zurückkehren {
        Anzahl: 1,
      }
    },
    Methoden: {
      ändern() {
        diese.Anzahl = 2
      },
    },
    berechnet: {
      Summe() {
        gib dies zurück.Anzahl + 1
      },
    },
  })
</Skript>

Berechnete Daten initialisieren

Wenn vue initialisiert wird, wird zuerst die Init-Methode ausgeführt, und der darin enthaltene InitState initialisiert die berechneten Eigenschaften

wenn (opts.computed) {initComputed(vm, opts.computed);}

Unten ist der Code für initComputed

var watchers = vm._computedWatchers = Object.create(null); 
// Definieren Sie nacheinander einen berechneten Beobachter für jede berechnete Eigenschaft
für (const Schlüssel in berechnet) {
  const userDef = berechnet[Schlüssel]
  Beobachter[Schlüssel] = neuer Beobachter(
      vm, // Instanzgetter, // vom Benutzer übergebene Auswertungsfunktion sum
      noop, // Callback-Funktion kann zunächst ignoriert werden { lazy: true } // Lazy-Attribut deklarieren um berechneten Watcher zu markieren
  )
  // Was passiert, wenn der Benutzer this.sum defineComputed(vm, key, userDef) aufruft?
}

Der Anfangszustand des Berechnungs-Watchers entsprechend jeder berechneten Eigenschaft ist wie folgt:

{
    abhängig von: [],
    schmutzig: wahr,
    Getter: ƒ sum(),
    faul: stimmt,
    Wert: undefiniert
}

Sie können sehen, dass sein Wert am Anfang nicht definiert ist und „lazy“ „true“ ist, was bedeutet, dass sein Wert verzögert berechnet wird und erst berechnet wird, wenn sein Wert tatsächlich in der Vorlage gelesen wird.

Dieses Dirty-Attribut ist tatsächlich der Schlüssel zum Caching, also merken Sie es sich zuerst.

Als nächstes schauen wir uns das kritischere defineComputed an, das bestimmt, was passiert, nachdem der Benutzer den Wert der berechneten Eigenschaft this.sum gelesen hat. Wir werden weiterhin einige Logik vereinfachen und ausschließen, die den Prozess nicht beeinflusst.

Object.defineProperty(Ziel, Schlüssel, { 
    erhalten() {
        // Den berechneten Watcher von der gerade erwähnten Komponenteninstanz holen
        const watcher = this._computedWatchers && this._computedWatchers[Schlüssel]
        wenn (Beobachter) {
          // Nur wenn es schmutzig ist, wird es neu ausgewertet, wenn (watcher.dirty) {
            // Dadurch wird get ausgewertet, aufgerufen und Dep.target festgelegt.
            Beobachter.auswerten()
          }
          // Auch das ist ein wichtiger Punkt und ich werde ihn später im Detail erklären if (Dep.target) {
            Beobachter.abhängig()
          }
          //Zum Schluss den berechneten Wert zurückgeben return watcher.value
        }
    }
})

Diese Funktion muss genauer betrachtet werden. Sie macht mehrere Dinge. Lassen Sie es uns anhand des Initialisierungsprozesses erklären:

Zunächst einmal stellt das Konzept „Dirty“ schmutzige Daten dar, was bedeutet, dass die Daten durch erneuten Aufruf der vom Benutzer übergebenen Summenfunktion ausgewertet werden müssen. Lassen wir die Update-Logik vorerst einmal außer Acht. Beim ersten Lesen von {{sum}} in der Vorlage muss es wahr sein, damit die Initialisierung eine Auswertung durchläuft.

auswerten() {
  //Rufen Sie die Get-Funktion auf, um this.value = this.get() auszuwerten
  //Dirty als falsch markieren
  this.dirty = falsch
}

Diese Funktion ist eigentlich sehr übersichtlich, sie wertet zuerst aus und setzt dann „dirty“ auf „false“. Schauen wir uns noch einmal die Logik von Object.defineProperty an. Wenn das nächste Mal die Summe ohne besondere Umstände gelesen wird und dirty falsch ist, können wir einfach den Wert von watcher.value zurückgeben. Dies ist eigentlich das Konzept des berechneten Eigenschafts-Cachings.

Abhängigkeitssammlung

Nach Abschluss der Initialisierung wird Render zum Rendern aufgerufen, und die Renderfunktion dient als Getter des Watchers. Zu diesem Zeitpunkt ist der Watcher der Rendering-Watcher.

updateComponent = () => {
  vm._update(vm._render(), hydratisieren)
}
// Rendering-Watcher erstellen. Wenn der Rendering-Watcher initialisiert wird, wird seine get()-Methode, also die Render-Funktion, aufgerufen, um Abhängigkeiten zu sammeln new Watcher(vm, updateComponent, noop, {}, true /* isRenderWatcher */)

Schauen Sie sich die Get-Methode im Watcher an.

erhalten () {
    //Lege den aktuellen Watcher ganz oben auf den Stapel und setze ihn auf Dep.target
    drückeZiel(dieses)
    let-Wert
    const vm = diese.vm
    // Durch Aufrufen der benutzerdefinierten Funktion wird auf this.count zugegriffen und damit auf die Getter-Methode, die weiter unten erläutert wird. value = this.getter.call(vm, vm)
    // Nachdem die Auswertung abgeschlossen ist, wird der aktuelle Watcher aus dem Stack entfernt popTarget()
    dies.cleanupDeps()
    Rückgabewert
 }

Wenn der Getter des Rendering-Watchers ausgeführt wird (Renderfunktion), wird auf this.sum zugegriffen, wodurch der Getter des berechneten Attributs ausgelöst wird, d. h. die in initComputed definierte Methode. Nachdem der berechnete Watcher an die Summe gebunden wurde, wird seine Auswertungsmethode aufgerufen, da dirty während der Initialisierung true ist, und schließlich wird seine Methode get() aufgerufen, um den berechneten Watcher oben auf dem Stapel abzulegen. Zu diesem Zeitpunkt ist Dep.target auch der berechnete Watcher.

Wenn Sie dann die Get-Methode aufrufen, wird auf this.count zugegriffen, wodurch der Getter des Count-Attributs ausgelöst wird (wie unten gezeigt) und der im aktuellen Dep.target gespeicherte Watcher in der dem Count-Attribut entsprechenden Dep gesammelt wird. An diesem Punkt ist die Auswertung abgeschlossen und popTarget() wird aufgerufen, um den Watcher aus dem Stapel zu entfernen. An diesem Punkt befindet sich der vorherige Rendering-Watcher ganz oben auf dem Stapel und Dep.target wird wieder zum Rendering-Watcher.

// Im Abschluss wird das für die Schlüsselanzahl definierte Dep beibehalten
const dep = new Dep()
 
// Der Abschluss behält auch den von der letzten Set-Funktion festgelegten Wert bei
lass val
 
Objekt.defineProperty(Objekt, Schlüssel, {
  get: Funktion reactiveGetter () {
    konstanter Wert = val
    // Dep.target berechnet nun den Watcher
    wenn (Dep.target) {
      // Abhängigkeiten sammeln dep.depend()
    }
    Rückgabewert
  },
})
// dep.abhängig()
abhängen() {
  wenn (Dep.target) {
    Dep.target.addDep(dieses)
  }
}
// addDep-Funktion des Beobachters addDep (dep: Dep) {
  // Zur Vereinfachung werden hier eine Reihe von Deduplizierungsvorgängen durchgeführt // Hier wird die Dep von count auch in ihren eigenen Deps gespeichert this.deps.push(dep)
  // Mit dem Watcher selbst als Parameter // Zurück zur addSub-Funktion von dep dep.addSub(this)
}
Klasse Dep {
  subs = []
 
  addSub (sub: Beobachter) {
    dies.subs.push(sub)
  }
}

Durch diese beiden Codeteile wird der berechnete Watcher durch das Attribut Bound Dep gesammelt. Der Watcher hängt vom Dep ab und das Dep hängt auch vom Watcher ab. Diese voneinander abhängige Datenstruktur kann leicht erkennen, von welchen Deps ein Watcher abhängt und von welchen Watchern ein Dep abhängt.

Führen Sie dann watcher.depend() aus

// Beobachter.abhängig
abhängen() {
  sei i = this.deps.length
  während (i--) {
    dies.deps[i].depend()
  }
}

Erinnern Sie sich an die Berechnung des Beobachterformulars von gerade eben? Seine Abhängigkeiten speichern die Abhängigkeit der Anzahl. Das heißt, dep.depend() wird bei count erneut aufgerufen.

Klasse Dep {
  subs = []
  
  abhängen() {
    wenn (Dep.target) {
      Dep.target.addDep(dieses)
    }
  }
}

Dieses Mal ist Dep.target bereits der Rendering-Watcher, sodass das Dep dieser Zählung den Rendering-Watcher in seinen eigenen Subs speichert.

Schließlich werden die Abhängigkeiten von count gesammelt und ihre Abhängigkeit lautet:

{
    Subs: [Summenberechnungs-Beobachter, Rendering-Beobachter]
}

Updates verteilen

Nun kommen wir zum Kernpunkt dieser Frage: Wie wird die Ansichtsaktualisierung ausgelöst, wenn der Zähler aktualisiert wird?

Kehren wir zur responsiven Hijacking-Logik von count zurück:

// Im Abschluss wird das für die Schlüsselanzahl definierte Dep beibehalten
const dep = new Dep()
 
// Der Abschluss behält auch den von der letzten Set-Funktion festgelegten Wert bei
lass val
 
Objekt.defineProperty(Objekt, Schlüssel, {
  set: Funktion reactiveSetter (newVal) {
      val = neuerWert
      // Benachrichtigung über die Abhängigkeit des Zählers auslösen
      dep.benachrichtigen()
    }
  })
})

Nun, hier wird die Benachrichtigungsfunktion des Count-Dep ausgelöst, die wir gerade sorgfältig vorbereitet haben.

Klasse Dep {
  subs = []
  
  benachrichtigen () {
    für (sei i = 0, l = Teillänge; i < l; i++) {
      subs[i].update()
    }
  }
}

Die Logik hier ist sehr einfach. Rufen Sie nacheinander die Update-Methode der in Subs gespeicherten Watcher auf, d. h.

  1. Aufruf zur Berechnung des Watcher-Updates
  2. Aufrufen des Updates des Render Watchers

Berechnen von Watcher-Updates

aktualisieren () {
  wenn (dies.lazy) {
    dies.schmutzig = wahr
  }
}

Setzen Sie einfach die Dirty-Eigenschaft des Berechnungsbeobachters auf „true“ und warten Sie ruhig auf den nächsten Lesevorgang (wenn die Renderfunktion erneut ausgeführt wird, wird erneut auf die Summeneigenschaft zugegriffen und „Dirty“ ist zu diesem Zeitpunkt „true“, sodass es erneut ausgewertet wird).

Aktualisierungen des Rendering-Watchers

Hier rufen wir tatsächlich die Funktion vm._update(vm._render()) auf, um die Ansicht entsprechend dem von der Renderfunktion generierten Vnode erneut zu rendern.
Beim Rendern wird auf den Wert von su zugegriffen, daher kehren wir zu dem durch sum definierten get zurück:

Object.defineProperty(Ziel, Schlüssel, { 
    erhalten() {
        const watcher = this._computedWatchers && this._computedWatchers[Schlüssel]
        wenn (Beobachter) {
          // Im vorherigen Schritt wurde dirty bereits auf true gesetzt, daher wird es erneut ausgewertet, wenn (watcher.dirty) {
            Beobachter.auswerten()
          }
          wenn (Dep.target) {
            Beobachter.abhängig()
          }
          //Zum Schluss den berechneten Wert zurückgeben return watcher.value
        }
    }
})

Aufgrund der reaktionsfähigen Eigenschaftsaktualisierung im vorherigen Schritt wird das Dirty Update des berechneten Watchers auf „True“ ausgelöst. Daher wird die vom Benutzer übergebene Summenfunktion erneut aufgerufen, um den neuesten Wert zu berechnen, und der neueste Wert wird natürlich auf der Seite angezeigt.

Damit ist der gesamte Prozess der Berechnung von Attributaktualisierungen abgeschlossen.

Um zusammenzufassen

  1. Initialisieren Sie die Daten und Berechnungen, leiten Sie deren Set- und Get-Methoden als Proxy weiter und generieren Sie eindeutige Dep-Instanzen für alle Eigenschaften in den Daten.
  2. Generieren Sie einen eindeutigen Watcher für die berechnete Summe und speichern Sie ihn in vm._computedWatchers
  3. Wenn die Renderfunktion ausgeführt wird, wird auf das Sum-Attribut zugegriffen. Dadurch wird die in initComputed definierte Getter-Methode ausgeführt, Dep.target auf den Watcher von Sum gerichtet und die spezifische Methode Sum des Attributs aufgerufen.
  4. Der Zugriff auf this.count in der Sum-Methode ruft die Get-Methode des this.count-Proxys auf, fügt die Abhängigkeit von this.count zum Watcher von Sum hinzu und fügt die Subs in der Abhängigkeit diesem Watcher hinzu.
  5. Setzen Sie vm.count = 2 und rufen Sie die Set-Methode des Count-Agenten auf, um die Benachrichtigungsmethode von dep auszulösen. Da es sich um eine berechnete Eigenschaft handelt, wird nur „dirty“ im Watcher auf „true“ gesetzt.
  6. Im letzten Schritt wird, wenn vm.sum auf seine Get-Methode zugreift, festgestellt, dass watcher.dirty von sum wahr ist, und seine Watcher.evaluate()-Methode wird aufgerufen, um den neuen Wert abzurufen.

Oben finden Sie eine ausführliche Erläuterung des Cache-Implementierungsprinzips von Vue Computed. Weitere Informationen zur Cache-Implementierung von Vue Computed finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Vue – Behebung des Fehlers. Der berechneten Eigenschaft „****“ wurde zwar ein Setter zugewiesen, sie verfügt jedoch nicht über diesen.
  • Was sind die Unterschiede zwischen „Computed“ und „Watch“ in Vue?
  • Detaillierte Erklärung der Beobachtung und Berechnung in Vue
  • Der Unterschied und die Verwendung von „watch“ und „computed“ in Vue
  • Unterschied zwischen berechnet und beobachtet in Vue
  • Der Unterschied und die Verwendung von Watch, berechnet und aktualisiert in Vue
  • Ein kurzes Verständnis des Unterschieds zwischen Vue-berechneten Eigenschaften und der Überwachung
  • Vue berechnetes Eigenschaftscodebeispiel
  • Eine kurze Diskussion über die Daten-, Berechnungs- und Überwachungsquellcodes von Vue
  • Warum können Datenänderungen und deren Unterschiede in Vue-Monitoren beobachtet und berechnet werden?
  • Der Unterschied und die Verwendung von Filter und berechnet in Vue

<<:  Tutorial zur Installation und Konfiguration der dekomprimierten Version von mysql5.7.19 winx64

>>:  Verwaltung privater Docker-Repositorys und Löschen von Bildern in lokalen Repositorys

Artikel empfehlen

Den praktischen Wert der CSS-Eigenschaft *-gradient erkunden

Lassen Sie mich zunächst eine interessante Eigens...

Details zum JavaScript-Prototyp

Inhaltsverzeichnis 1. Übersicht 1.1 Was ist ein P...

So zeichnen Sie spezielle Grafiken in CSS

1. Dreieck Rahmeneinstellungen Code: Breite: 300p...

Vue+Router+Element zur Implementierung einer einfachen Navigationsleiste

Dieses Projekt teilt den spezifischen Code von Vu...

Die Verwendung von Textbereichen in HTML und häufige Probleme und Fallanalyse

Der Textarea-Tag ist ein HTML-Tag, den wir häufig ...

Grundlegender Installationsprozess von mysql5.7.19 unter winx64 (Details)

1. Herunterladen https://dev.mysql.com/downloads/...

MySQL InnoDB-Quellcodeanalyse für Transaktionssperren

Inhaltsverzeichnis 1. Schloss und Riegel 2. Wiede...

Lösung für die langsame Reaktion des Tomcat-Servers

1. Analytisches Denken 1. Beseitigen Sie die eige...

Prozessdiagramm für die Sitzungsfreigabe bei Tomcat Nginx Redis

1. Vorbereitung Middleware: Tomcat, Redis, Nginx ...

Gängige Master-Slave-Replikationsarchitekturen in MySQL 4

Inhaltsverzeichnis Replikationsarchitektur mit ei...

Versuchen Sie Docker+Nginx, um die Single-Page-Anwendungsmethode bereitzustellen

Von der Entwicklung bis zur Bereitstellung: Mache...

Shell-Skripteinstellungen zum Verhindern von Brute-Force-SSH

Das Shell-Skript richtet die Zugriffskontrolle ei...