Manuelle Implementierung des bidirektionalen Datenbindungsprinzips von Vue2.0

Manuelle Implementierung des bidirektionalen Datenbindungsprinzips von Vue2.0

In einem Satz: Datenentführung (Object.defineProperty) + Publish-Subscribe-Modus

Es gibt drei Kernmodule für die bidirektionale Datenbindung (Dep, Observer, Watcher). Lassen Sie uns nacheinander erklären, wie sie miteinander verbunden sind.

Um das Prinzip der bidirektionalen Datenbindung und die damit verbundenen Zusammenhänge besser zu verstehen, sehen wir uns zunächst das Publish-Subscribe-Modell an.

1. Verstehen Sie zunächst, was das Publish-Subscribe-Modell ist

Direkt zum Code:

Ein einfaches Publish-Subscribe-Modell, das Ihnen hilft, das Prinzip der bidirektionalen Datenbindung besser zu verstehen

//Veröffentlichungs- und Abonnementmodus Funktion Dep() {
  this.subs = []//Abhängigkeiten sammeln (also Mobile Watcher Instanzen),
}
Dep.prototype.addSub = function (sub) { //Abonnenten hinzufügen this.subs.push(sub); //Eigentlich wird die Watcher-Instanz hinzugefügt}
Dep.prototype.notify = function (sub) { //Veröffentlichen, diese Methode wird verwendet, um das Array zu durchlaufen und die Aktualisierungsmethode jedes Abonnenten this.subs.forEach((sub) => sub.update()) ausführen zu lassen.
}

Funktion Watcher(fn) {
  dies.fn = fn;
}
Watcher.prototype.update = function () { //Fügen Sie eine Update-Eigenschaft hinzu, damit jede Instanz diese Methode erben kann this.fn();
}
let watcher = neuer Watcher(Funktion () {
  Alarm(1)
}); //Abonnement let dep = new Dep();
dep.addSub(watcher); //Abhängigkeit hinzufügen, Abonnenten hinzufügen dep.notify(); //Veröffentlichen, die Update-Methode jedes Abonnenten ausführen lassen

2. Was hat new Vue() gemacht?

Nur um die bidirektionale Datenbindung zu veranschaulichen

<Vorlage>
   <div id="app">
    <div>Der Wert von obj.text:{{obj.text}}</div>
    <p>Der Wert von Wort:{{Wort}}</p>
    <Eingabetyp="Text" v-Modell="Wort">
  </div>
</Vorlage>
<Skript>
  neuer Vue({
    el: "#app",
    Daten: {
      Objekt: {
        Text: "Nach oben",
      },
      Wort: „Lernen“
    },
    Methoden:{
    // ...
    }
  })
</Skript>

Was macht der Vue-Konstruktor?

Funktion Vue(Optionen = {}) {
  this.$options = options; //Parameter empfangen var data = this._data = this.$options.data;
  Beobachter(Daten); //Binde die Daten rekursiv in Daten für (let-Schlüssel in Daten) {
    let val = Daten[Schlüssel];
    Beobachter(Wert);
    Object.defineProperty(dieser, Schlüssel, {
      aufzählbar: wahr,
      erhalten() {
        gib dies zurück._data[Schlüssel];
      },
      setze(neuerWert) {
        this._data[Schlüssel] = neuerWert;
      }
    })
  }
  neu Kompilieren(options.el, this)
};

Holen Sie sich im neuen Vue({…})-Konstruktor zuerst die Parameteroptionen und weisen Sie dann die Daten im Parameter der _data-Eigenschaft der aktuellen Instanz zu (this._data = this.$options.data). Hier kommt der Punkt, warum ist die folgende Durchquerung erforderlich? Wenn wir Daten verarbeiten, erhalten wir sie zunächst von this.word statt von this._data.word, sodass wir eine Zuordnung vornehmen. Beim Abrufen von Daten erhält this.word tatsächlich den Wert von this._data.word. Sie können dies in Ihrem Projekt ausgeben, um es zu überprüfen.

1. Als nächstes schauen wir uns an, was die Beobachtermethode macht

Funktion Beobachter(Daten) {
  wenn (Datentyp !== "Objekt") return;
  returniere neuen Observer(data); //Gib eine Instanz zurück}
Funktion Beobachter(Daten) {
  let dep = new Dep(); //Erstellen Sie eine Dep-Instanz für (let-Schlüssel in Daten) { //Binden Sie die Daten rekursiv let val = data[Schlüssel];
    Beobachter(Wert);
    Object.defineProperty(Daten, Schlüssel, {
      aufzählbar: wahr,
      erhalten() {
        Dep.target && dep.depend(Dep.target); //Dep.target ist eine Instanz von Watcher return val;
      },
      setze(neuerWert) {
        wenn (neuerWert === Wert) {
          zurückkehren;
        }
        val = neuerWert;
        Beobachter(neuerWert);
        dep.notify() //Alle Methoden ausführen lassen}
    })
  }
}

Observer-Konstruktor: Lassen Sie zunächst dep = new Dep() als Get-Methode und Set-Methode, die Datenentführung auslösen, Abhängigkeiten sammeln und beim Veröffentlichen aufrufen. Die Hauptoperation besteht darin, Daten rekursiv über Object.defineProperty zu binden und Getter/Setter zu verwenden, um deren Standardlese- und -schreibvorgänge zum Sammeln von Abhängigkeiten und Veröffentlichen von Updates zu ändern.

2. Schauen wir uns an, was Compile konkret macht

Funktion Kompilieren(el, vm) {
  vm.$el = document.querySelector(el);
  let fragment = document.createDocumentFragment(); //Dokumentfragment erstellen, das vom Objekttyp istwhile (child = vm.$el.firstChild) {
    fragment.appendChild(Kind);
  }; //Verwenden Sie eine while-Schleife, um alle Knoten zum Dokumentfragment hinzuzufügen, führen Sie anschließend Operationen am Dokumentfragment aus und fügen Sie das Dokumentfragment schließlich zur Seite hinzu. Eine sehr wichtige Funktion hierbei ist, dass der ursprüngliche Knoten gelöscht wird, wenn die Methode appendChid verwendet wird, um einem Fragment einen Knoten im ursprünglichen DOM-Baum hinzuzufügen.
  ersetzen (Fragment);

  Funktion ersetzen(Fragment) {
    Array.from(fragment.childNodes).forEach((node) => {//Alle Knoten durchgehen let text = node.textContent;
      sei reg = /\{\{(.*)\}\}/;
      if (node.nodeType === 3 && reg.test(text)) {//Beurteilen Sie, ob der aktuelle Knoten ein Textknoten ist und ob er der Ausgabemethode von {{obj.text}} entspricht. Wenn die Bedingungen erfüllt sind, bedeutet dies, dass es sich um eine bidirektionale Datenbindung handelt und ein Abonnent (Watcher) hinzugefügt werden muss.
        konsole.log(RegExp.$1); //obj.text
        let arr = RegExp.$1.split("."); //In Array [Obj, Text] konvertieren, um Werte einfach abzurufen let val = vm;
        arr.forEach((key) => { //Ermittle den Wert this.obj.text
          val = val[Schlüssel];
        });
        neuer Watcher(vm, RegExp.$1, Funktion (neuerWert) {
          node.textContent = text.replace(/\{\{(.*)\}\}/, neuerWert)
        });
        node.textContent = text.replace(/\{\{(.*)\}\}/, val); //Weisen Sie dem Knoteninhalt den Anfangswert zu}
      if (node.nodeType === 1) { //Zeigt an, dass es sich um ein Elementknoten handelt let nodeAttrs = node.attributes;
        Array.from(nodeAttrs).fürEach((item) => {
          if (item.name.indexOf("v-") >= 0) {//Beurteilen, ob es sich um eine v-Modell-Anweisung handelt node.value = vm[item.value]//Dem Knoten einen Wert zuweisen}
          //Abonnenten hinzufügen new Watcher(vm, item.value, function (newVal) {
            Knoten.Wert = vm[Element.Wert]
          });
          node.addEventListener("Eingabe", Funktion (e) {
            sei neuer Wert = e.Zielwert;
            vm[item.value] = neuerWert;
          })
        })
      }
      if (node.childNodes) { //Dieser Knoten hat noch untergeordnete Elemente, rekursiv replace(node);
      }
    })
  }

  //Das liegt daran, dass das Dokument auf der Seite weg ist, also müssen wir das Dokumentfragment in die Seite einfügen vm.$el.appendChild(fragment);

}

Kompilieren

Lassen Sie mich zunächst DocumentFragment erklären. Es handelt sich um einen DOM-Knotencontainer. Wenn Sie mehrere Knoten erstellen, löst jeder Knoten einen Reflow aus, wenn er in das Dokument eingefügt wird. Das heißt, der Browser muss mehrere Male einen Reflow durchführen, was sehr leistungsintensiv ist. Bei der Verwendung von Dokumentfragmenten werden zuerst mehrere Knoten in einen Container eingefügt und dann der gesamte Container direkt eingefügt. Der Browser führt nur einmal einen Reflow durch.

Die Kompilierungsmethode durchläuft zuerst alle Knoten des Dokumentfragments. 1. Bestimmen Sie, ob es sich um einen Textknoten handelt und ob er der Ausgabemethode mit doppelten geschweiften Klammern von {{obj.text}} entspricht. Wenn die Bedingungen erfüllt sind, bedeutet dies, dass es sich um eine bidirektionale Datenbindung handelt und ein Abonnent (Watcher) hinzugefügt werden muss, neuer Watcher (VM, dynamisch gebundene Variablen, Rückruffunktion fn). 2. Bestimmen Sie, ob es sich um einen Elementknoten handelt und ob das Attribut die v-Modell-Anweisung enthält. Wenn die Bedingungen erfüllt sind, bedeutet dies, dass es sich um eine bidirektionale Datenbindung handelt und ein Abonnent (Watcher) hinzugefügt werden muss, neuer Watcher (VM, dynamisch gebundene Variablen, Rückruffunktion fn), bis die Durchquerung abgeschlossen ist.

Vergessen Sie nicht, die Dokumentfragmente in die Seite einzufügen.

3.Dep-Konstruktor (wie man Abhängigkeiten sammelt)

var uid=0;
//Veröffentlichen und Abonnieren Funktion Dep() {
  diese.id=uid++;
  dies.subs = [];
}
Dep.prototype.addSub = function (sub) { //Abonnieren Sie this.subs.push(sub); //Eigentlich wird die Watcher-Instanz hinzugefügt}
Dep.prototype.depend = function () { // Abonnement-Manager if(Dep.target){//Nur wenn Dep.target vorhanden ist, wird es hinzugefügt Dep.target.addDep(this);
  }
}
Dep.prototype.notify = function (sub) { //Veröffentlichen, das Array durchlaufen und die Update-Methode jedes Abonnenten ausführen lassen this.subs.forEach((sub) => sub.update())
}

Es gibt eine ID und ein Subs innerhalb des Dep-Konstruktors, id=uid++, die ID wird als eindeutige Kennung des Dep-Objekts verwendet und Subs ist das Array, das den Watcher speichert. Die Depend-Methode ist ein Abonnementmanager. Sie ruft die addDep-Methode des aktuellen Beobachters auf, um Abonnenten hinzuzufügen. Wenn die get-Methode der Datenentführung (Object.defineProperty) ausgelöst wird, werden Dep.target && dep.depend(Dep.target) aufgerufen, um Abonnenten hinzuzufügen. Wenn die set-Methode der Datenentführung (Object.defineProperty) bei einer Datenänderung ausgelöst wird, wird die dep.notify-Methode aufgerufen, um den Vorgang zu aktualisieren.

4. Was macht der Watcher-Konstruktor?

Funktion Watcher(vm, exp, fn) {
  dies.fn = fn;
  dies.vm = vm;
  dies.exp = exp //
  dies.newDeps = [];
  this.depIds = neues Set();
  this.newDepIds = neues Set();
  Dep.target = this; //dies bezieht sich auf die aktuelle (Watcher-)Instanz let val = vm;
  lass arr = exp.split(".");
  arr.forEach((k) => { //den Wert von this.obj.text abrufen
    val = val[k] // Wenn der Wert von this.obj.text übernommen wird, wird die Get-Methode des Daten-Hijackings ausgelöst und der aktuelle Teilnehmer (Watcher-Instanz) zur Abhängigkeit hinzugefügt});
  Dep.Ziel = null;
}
Watcher.prototype.addDep = Funktion (dep) {
  var id=dep.id;
  wenn (!this.newDepIds.has(id)){
    dies.newDepIds.add(id);
    dies.newDeps.push(dep);
    wenn(!this.depIds.has(id)){
      dep.addSub(dies);
    }
  }
 
}
Watcher.prototype.update = function () { //So fügt jede gebundene Methode eine Update-Eigenschaft hinzu let val = this.vm;
  lass arr = this.exp.split(".");
  arr.fürEach((k) => { 
    val = val[k] //Holen Sie sich den Wert von this.obj.text und übergeben Sie ihn an fn für den Aktualisierungsvorgang});
  this.fn(val); // einen neuen Wert übergeben}

Was macht der Watcher-Konstruktor?

1 Empfängt Parameter und definiert mehrere private Eigenschaften (this.newDep, this.depIds
,diese.neuenDepIds)

2. Dep.target = hier wird die Datenwertoperation über den Parameter ausgeführt, wodurch die Get-Methode von Object.defineProperty ausgelöst wird, die Abonnenten über den Abonnenten-Manager (dep.depend ()) hinzufügt und dann Dep.target = null auf leer setzt.

3. addDep im Prototyp verwendet die eindeutige Kennung id und mehrere private Eigenschaften, um zu verhindern, dass Abonnenten wiederholt hinzugefügt werden

4. Die Aktualisierungsmethode ist die, bei der die Daten aktualisiert werden. Dep.notify() wird ausgeführt, wodurch die Aktualisierungsmethode des Abonnenten ausgelöst wird, um den Veröffentlichungsaktualisierungsvorgang auszuführen.

Um zusammenzufassen

In vue2.0 besteht die bidirektionale Datenbindung aus drei Teilen: Observer, Watcher und Dep.

1. Verwenden Sie zunächst Object.defineProperty(), um Datenentführung rekursiv zu implementieren, und weisen Sie jeder Eigenschaft ein Verwaltungs-Array-Dep der Abonnentensammlung zu.

2. Erstellen Sie beim Kompilieren ein Dokumentfragment, fügen Sie alle Knoten zum Dokumentfragment hinzu, durchlaufen Sie alle Knoten des Dokumentfragments, wenn es sich um {{}}, v-Modell, neue Watcher ()-Instanz handelt, und fügen Sie die Instanz zum Subs-Array von dep hinzu

3. Die endgültige Änderung des Wertes löst die Set-Methode von Object.defineProperty () aus, in der dep.notify () ausgeführt wird. Anschließend wird die Update-Methode aller Abonnenten in einer Schleife aufgerufen, um die Ansicht zu aktualisieren.

Dies ist das Ende dieses Artikels über die manuelle Implementierung des bidirektionalen Datenbindungsprinzips von Vue2.0. Weitere relevante Inhalte zur bidirektionalen Datenbindung von Vue2.0 finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Vue2.0 verwendet v-model, um eine schöne Lösung für die bidirektionale Bindung von Komponenteneigenschaften zu implementieren
  • Analysieren Sie das Implementierungsprinzip der bidirektionalen Bindung von Vue2.0
  • Zweiwegebindung von Vue2.0-Daten und Formular-Bootstrap+Vue-Komponenten
  • Vue2.0 implementiert die bidirektionale Bindung von Komponentendaten
  • Detaillierte Erläuterung des Implementierungsprinzips der bidirektionalen Datenbindung von Vue2.0/3.0
  • Verwenden von js zum Implementieren der bidirektionalen Bindungsfunktion von Daten in Vue2.0

<<:  Detaillierte Erläuterung der MySQL Master-Slave-Replikationspraxis - Replikation basierend auf Protokollpunkten

>>:  Detaillierte Erläuterung der WordPress-Multisite-Konfiguration in der Nginx-Umgebung

Artikel empfehlen

js zur Realisierung der Web-Message-Board-Funktion

In diesem Artikelbeispiel wird der spezifische JS...

Detailliertes Tutorial zur Springcloud-Alibaba-Nacos-Linux-Konfiguration

Laden Sie zuerst das komprimierte Nacos-Paket von...

Detaillierte Erklärung der CSS-Float-Eigenschaft

1. Was ist Floating? Floaten bedeutet, wie der Na...

Detaillierte Erläuterung des Vuex-Gesamtfalls

Inhaltsverzeichnis 1. Einleitung 2. Vorteile 3. N...

Inaktive Benutzer nach einem Login-Timeout in Linux automatisch abmelden

Methode 1: Ändern Sie die Datei .bashrc oder .bas...

Diskussion über Web-Nachahmung und Plagiat

Einige Monate nachdem ich 2005 in die Branche eing...

JS implementiert einfache Addition und Subtraktion von Warenkorbeffekten

In diesem Artikelbeispiel wird der spezifische JS...

Docker stellt MySQL bereit, um Beispielcode für eine Remoteverbindung zu erreichen

1. Docker durchsucht MySQL查看mysql版本 2. Docker Pul...