Tiefgreifendes Verständnis der Verwendung von Vue

Tiefgreifendes Verständnis der Verwendung von Vue

Verstehen Sie das Kernkonzept von Vue

Die Verwendung von Vue macht Menschen körperlich und geistig glücklich. Es bietet die Vorteile von Angular und React. Es ist leichtgewichtig, hat eine einfache API, vollständige Dokumentation, ist einfach und leistungsstark und bietet alles.

Wenn ich Vue in einem Satz zusammenfassen sollte, fällt mir als erstes ein Satz aus der offiziellen Dokumentation ein:

Vue.js (ausgesprochen /vjuː/, ähnlich wie „view“) ist ein fortschrittliches Framework zum Erstellen von Benutzeroberflächen.

Dieser Satz ist vielleicht jedem bekannt, aber nicht viele Leute verstehen ihn wirklich. Wenn Sie diesen Satz verstehen, verstehen Sie tatsächlich das Kernkonzept von Vue.

Wie verstehen wir also, was ein progressives Framework ist? Zuvor müssen wir zunächst verstehen, was ein Framework ist. Um bei der anfänglichen Front-End-Entwicklung eine bestimmte Funktion abzuschließen, müssen wir den DOM-Knoten auf der HTML-Seite über JS abrufen und dann den Textinhalt im DOM-Knoten abrufen oder dem DOM-Knoten Ereignisse hinzufügen, um eine Reihe von Programmvorgängen auszuführen. Wenn das Aufgabenvolumen jedoch groß ist, wird der Code mit zunehmendem Geschäft aufgebläht und chaotisch. Bei der tatsächlichen Entwicklung können die komplexe Logik und das enorme Entwicklungsvolumen nicht mit nativem JS abgeschlossen werden.

Zu diesem Zeitpunkt unterteilt der Entwickler den JS-Code in drei Abschnitte: Daten (Modell), Logiksteuerung (*) und Ansicht (Ansicht). Der Datenabschnitt ist nur für den Datenteil verantwortlich, der Ansichtsabschnitt ist für die Änderung des Stils verantwortlich und die Logiksteuerung ist für die Verbindung des Ansichtsabschnitts und des Datenabschnitts verantwortlich. Dies hat große Vorteile. Wenn sich die Anforderungen ändern, müssen Sie nur den entsprechenden Abschnitt ändern.

Dieses Entwicklungsmodell ist die sogenannte MV*-Struktur. Die uns heute bekannten MVC-, MVP- und MVVM-Modelle sind alle Ableitungen von MV*. Durch den Vergleich dieser Rahmenmodelle können wir ein wesentliches Merkmal zusammenfassen: Diese Entwicklungsmodelle verhindern den direkten Kontakt zwischen Ansichten und Daten. Wenn Sie den Vorgang zum Abrufen von DOM mit nativem JS vergleichen, werden Sie feststellen, dass der native DOM-Stream tatsächlich DOM als Daten verwendet, das Modell von DOM abruft und dann DOM ändert, um die Ansicht zu aktualisieren. Die Ansicht und das Modell sind tatsächlich miteinander vermischt, sodass der Code natürlich chaotisch und schwer zu warten ist.

In einer Vue-Instanz mit einem reaktionsfähigen System ist der DOM-Status nur eine Abbildung des Datenstatus, d. h. UI = VM (Status). Wenn sich der Status auf der rechten Seite der Gleichung ändert, ändert sich die Benutzeroberfläche des Seitenanzeigeteils entsprechend. Aus diesem Grund finden viele Leute Vue bei der ersten Verwendung sehr benutzerfreundlich. Die Kernpositionierung von Vue ist jedoch kein Framework und sein Design folgt nicht vollständig dem MVVM-Modell. Sie können sehen, dass es in der Abbildung nur zwei Teile gibt: Status und Ansicht. Die Kernfunktion von Vue betont die Zuordnung des Status zur Schnittstelle und achtet nicht auf die strukturelle Organisation des Codes. Wenn daher nur seine Kernfunktionen verwendet werden, ist es kein Framework, sondern eher eine Ansichtsvorlagen-Engine. Aus diesem Grund haben die Vue-Entwickler es „View“ genannt, was ähnlich wie „View“ klingt.

Wie oben erwähnt, besteht die Kernfunktion von Vue in einer Ansichtsvorlagen-Engine. Dies bedeutet jedoch nicht, dass Vue nicht als Framework verwendet werden kann. Wie in der folgenden Abbildung gezeigt, sind hier alle Komponenten von Vue enthalten. Basierend auf deklarativem Rendering (View Template Engine) können wir ein vollständiges Framework erstellen, indem wir Komponentensystem, Client-Routing und umfangreiches Statusmanagement hinzufügen. Noch wichtiger ist, dass diese Funktionen voneinander unabhängig sind. Sie können andere Komponenten basierend auf den Kernfunktionen auswählen, ohne sie alle miteinander integrieren zu müssen. Wie Sie sehen, ist das sogenannte „progressiv“ tatsächlich die Art und Weise, Vue zu verwenden, und es spiegelt auch das Designkonzept von Vue wider.

Entdecken Sie das Prinzip und die Implementierung der bidirektionalen Bindung von Vue

Ergebnisse

Die folgenden zwei Inhalte werden vorgestellt

1. Das Prinzip der bidirektionalen Bindung von Vue

2. Der Prozess der Implementierung einer vereinfachten Version von Vue, einschließlich deklarativer Datendarstellung und einiger einfacher Anweisungen

Vue-Zweiwegebindungsprinzip

Die bidirektionale Bindung von Vue wird durch die Kombination von Datenentführung mit dem Publisher-Subscriber-Modell erreicht. Was also ist Datenentführung? Wie kapert Vue Daten? Einfach ausgedrückt geht es darum, die Setter- und Getter-Operationen von Objekteigenschaften über Object.defineProperty() zu kapern und das zu tun, was Sie tun möchten, wenn sich die Daten ändern. Wir können uns über die Konsolen-Comb ansehen, was ein in den Vue-Initialisierungsdaten definiertes Objekt ist.

var vm = neuer Vue({
    Daten: {
        prüfen : {
            ein: 1
        }
    },
    erstellt: Funktion () {
        konsole.log(dieser.test);
    }
});

Druckergebnisse:

Im Druckergebnis können wir sehen, dass Attribut a zwei Methoden hat: get und set. Warum gibt es diese beiden Methoden? Genau so kapert Vue Daten über Object.defineProperty().

Was macht die Methode Object.defineProperty()? In der Dokumentation heißt es dazu

Einfach ausgedrückt kann es einige einzigartige Operationen der Eigenschaften eines Objekts steuern, wie z. B. Lese- und Schreibrechte und ob es aufzählbar ist. Hier untersuchen wir hauptsächlich seine Get- und Set-Methoden. Wenn Sie mehr über seine Verwendung erfahren möchten, können Sie hier nachlesen: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Wir können die Attributdaten eines Objekts einfach ausdrucken:

var Buch = {
  Name: „Menschliche Schwäche“
};
console.log(Buch.name); // Menschliche Schwäche

Was aber, wenn wir während der Ausführung von console.log(Book.name) Anführungszeichen zum Buchtitel hinzufügen möchten? Wir müssen Object.defineProperty() verwenden:

// Gleichzeitig mit console.log(book.name) direkt eine Buchnummer zum Buch hinzufügen var Book = {};
Variablenname = '';
Objekt.defineProperty(Buch,'Name',{
    setze:Funktion(Wert) {
        Name = Wert;
        console.log('Sie haben einen Buchnamen gewählt:' + Wert);
    },
    bekomme:Funktion() {
        console.log('get-Methode wird überwacht');
        gibt '<'+name+'>' zurück;
    }
});
Book.name = 'Schwäche der menschlichen Natur'; //Sie haben ein Buch benannt: Schwäche der menschlichen Naturconsole.log(Book.name); //<Schwäche der menschlichen Natur>

Die Namenseigenschaft des Book-Objekts wird über die Methode Object.defineProperty () festgelegt, und ihre Get- und Set-Methoden werden neu geschrieben. Die Get-Methode wird aufgerufen, wenn die Namenseigenschaft abgerufen wird, und die Set-Methode wird ausgelöst, wenn die Namenseigenschaft festgelegt wird. Wenn Sie also die Anweisung Book.name = 'Weakness of Human Nature' ausführen, wird die Set-Methode aufgerufen, um den von Ihnen gewählten Buchtitel auszugeben: Weakness of Human Nature. Wenn console.log(Book.name) aufgerufen wird, wird die get-Methode ausgelöst und die Ausgabe lautet „Schwäche der menschlichen Natur“. Was wird gedruckt, wenn dieser Satz dem Code hinzugefügt wird?

console.log(Buch)

Die Ergebnisse sind wie folgt:

Dies ist dem Vergleich mit den Vue-Druckdaten oben sehr ähnlich und zeigt, dass Vue auf diese Weise Daten kapert. Was ist also das Publisher-Subscriber-Muster? ?

Abonnenten- und Herausgebermuster werden normalerweise in Nachrichtenwarteschlangen verwendet. Es gibt im Allgemeinen zwei Möglichkeiten, Nachrichtenwarteschlangen zu implementieren. Eine besteht darin, Produzenten und Konsumenten zu verwenden, die andere ist die Verwendung des Abonnenten-Herausgeber-Modells. Die Art und Weise, wie Abonnenten und Herausgeber Nachrichtenwarteschlangen implementieren, verwendet das Abonnentenmodell.

So sind beispielsweise die sogenannten Abonnenten genau das, was wir im Alltag mit unseren Zeitungsabonnements machen. Wenn wir eine Zeitung abonnieren, müssen wir uns normalerweise bei der Zeitungsredaktion oder einer Zwischenagentur registrieren. Wenn eine neue Ausgabe der Zeitung erscheint, muss der Postbote die Zeitungen der Reihe nach an die Abonnenten der Zeitung verteilen.

Die sogenannten Abonnenten sind genau wie wir, die im täglichen Leben Zeitungen abonnieren. Wenn wir eine Zeitung abonnieren, müssen wir uns normalerweise bei der Zeitung oder einer Zwischenagentur registrieren. Wenn eine neue Ausgabe einer Zeitung erscheint, muss der Postbote die Zeitung an die Abonnenten verteilen.

Wenn Sie dieses Muster in Code implementieren möchten, müssen Sie zwei Schritte ausführen:

1. Initialisieren Sie Herausgeber und Abonnenten.

2. Abonnenten müssen sich beim Herausgeber registrieren. Wenn der Herausgeber eine Nachricht veröffentlicht, veröffentlicht er diese Nachricht wiederum an die Abonnenten.

Abonnenten-Registrierung

Herausgeber veröffentlicht Nachricht

Anschließend implementieren wir eine einfache MVVM-Zweiwegebindungsdemo nach dem Vue-Prinzip

Gedankenanalyse

Bei der Implementierung von MVVM gibt es zwei Hauptaspekte: Aktualisieren der Daten, wenn sich die Ansicht ändert, und Aktualisieren der Ansicht, wenn sich die Daten ändern.

Tatsächlich kann die Aktualisierung der Daten bei einer Änderung der Ansicht durch eine Ereignisüberwachung erreicht werden, z. B. indem das Eingabe-Tag auf das Eingabeereignis hört. Daher konzentrieren wir uns auf die Analyse der Datenänderung, um die Ansicht zu aktualisieren.

Der Schlüssel zum Aktualisieren der Ansicht bei Datenänderungen besteht darin, zu erkennen, wann sich die Ansicht geändert hat. Solange Sie wissen, wann sich die Ansicht geändert hat, ist der Rest leicht zu handhaben. An diesem Punkt kommt das oben erwähnte Object.defineProperty() ins Spiel. Über Object.defineProperty() wird eine Set-Funktion für die Eigenschaft festgelegt. Diese Funktion wird ausgelöst, wenn sich die Eigenschaft ändert. Daher müssen wir nur einige Aktualisierungsmethoden in die Set-Funktion einfügen, um die Ansicht zu aktualisieren, wenn sich die Daten ändern.

Implementierungsprozess

Wir wissen bereits, wie man eine bidirektionale Datenbindung implementiert. Daher müssen wir zuerst die Daten entführen und überwachen. Dazu müssen wir zunächst einen Listener-Beobachter einrichten, der alle Eigenschaften überwacht. Wenn sich die Eigenschaft ändert, muss der Abonnenten-Beobachter benachrichtigt werden, um zu sehen, ob sie aktualisiert werden muss. Da es mehrere Attribute geben kann, wird es auch mehrere Abonnenten geben. Daher benötigen wir ein Nachrichtenabonnenten-Dep, um diese Abonnenten gezielt zu erfassen und eine einheitliche Verwaltung zwischen dem Listener-Beobachter und dem Abonnenten-Watcher durchzuführen. Da es einige Anweisungen für die Knotenelemente geben kann, benötigen wir auch einen Anweisungsparser, um jedes Knotenelement zu scannen und zu analysieren, die entsprechenden Anweisungen in einem Abonnenten-Watcher zu initialisieren, die Vorlagendaten zu ersetzen und die entsprechende Funktion zu binden. Wenn der Abonnenten-Watcher zu diesem Zeitpunkt die Änderung des entsprechenden Attributs empfängt, führt er die entsprechende Aktualisierungsfunktion aus, um die Ansicht zu aktualisieren.

Um die oben genannten Ideen zu organisieren, müssen wir drei Schritte implementieren, um die bidirektionale Bindung abzuschließen:

1. Implementieren Sie einen Observer, der alle Eigenschaften übernimmt und überwacht und Abonnenten bei Änderungen benachrichtigt.

2. Implementieren Sie einen Abonnenten-Watcher, der Benachrichtigungen über Eigenschaftsänderungen empfangen und entsprechende Funktionen zum Aktualisieren der Ansicht ausführen kann.

3. Implementieren Sie einen Parser-Kompiler, der die relevanten Anweisungen jedes Knotens scannen und analysieren und die entsprechenden Abonnenten basierend auf den Initialisierungsvorlagendaten initialisieren kann.

Das Flussdiagramm sieht wie folgt aus:

1. Implementieren Sie einen Listener Observer

Die Kernmethode des Datenlisteners ist Object.defineProperty(), die alle Eigenschaftswerte abhört, indem sie die Schleife durchläuft und Object.defineProperty() auf ihnen ausführt. Der Code kann wie folgt geschrieben werden:

//Für alle Attribute, rekursiv alle Attribute durchlaufen function defineReactive(data,key,val) {
    observe(val); //Alle Eigenschaften rekursiv durchlaufen Object.defineProperty(data,key,{
        enumerable:true, //Der Eigenschaftsdeskriptor kann nur geändert werden, wenn die Konfigurierbarkeit der Eigenschaft wahr ist, und die Eigenschaft kann auch aus dem entsprechenden Objekt gelöscht werden.
        configurable:true, //Diese Eigenschaft kann nur dann in der Enumerationseigenschaft des Objekts erscheinen, wenn das Enumerable dieser Eigenschaft true ist get:function() {
            Rückgabewert;
        },
        setze:Funktion(neuerWert) {
            val = neuerWert;
            console.log('Die Eigenschaft '+key+' wurde überwacht und der aktuelle Wert ist: "'+newVal.toString()+'"');
        }
    })
}

Funktion beobachten(Daten) {
    wenn(!Daten || Datentyp !== 'Objekt') {
        zurückkehren;
    }
    Objekt.Schlüssel(Daten).fürJeden(Funktion(Schlüssel){
        defineReactive(Daten, Schlüssel, Daten[Schlüssel]);
    });
}

var Bibliothek = {
    Buch1: {
        Name: ''
    },
    Buch2: ''
};
beobachten (Bibliothek);
library.book1.name = 'vue authoritative guide'; // Der Eigenschaftsname wurde überwacht und sein aktueller Wert ist: „vue authoritative guide“
library.book2 = ‚Kein solches Buch‘; // Das Attribut book2 wurde überwacht und der aktuelle Wert ist: „Kein solches Buch“

Durchsuchen Sie die Liste nach unten, um mit der Methode „object()“ alle Attribute zu finden, und führen Sie mit der Methode „defineReactive()“ eine Überwachung des Datendiebstahls durch.

In der obigen Idee benötigen wir einen Nachrichtenabonnenten-Dep, der Nachrichtenabonnenten aufnehmen kann. Der Abonnent sammelt hauptsächlich Nachrichtenabonnenten und führt dann die Aktualisierungsfunktion des entsprechenden Abonnenten aus, wenn sich das Attribut ändert. Daher muss der Nachrichtenabonnenten-Dep über einen Container zum Speichern von Nachrichtenabonnenten verfügen. Wir modifizieren den obigen Listener Observer leicht:

Funktion defineReactive(Daten,Schlüssel,Wert) {
    beobachte(Wert);
    var dep = neues Dep();
    Object.defineProperty(Daten, Schlüssel, {
        aufzählbar: wahr,
        konfigurierbar: true,
        bekomme: Funktion() {
            if (Müssen Sie einen Abonnenten hinzufügen) { //Watcher-Initialisierungstrigger dep.addSub(watcher); // Fügen Sie hier einen Abonnenten hinzu}
            Rückgabewert;
        },
        setze: Funktion(neuerWert) {
            wenn (Wert === neuerWert) {
                zurückkehren;
            }
            val = neuerWert;
            console.log('property' + key + ' wurde überwacht und der aktuelle Wert ist: "' + newVal.toString() + '"');
            dep.notify(); // Wenn sich die Daten ändern, alle Abonnenten benachrichtigen}
    });
}

Funktion beobachten(Daten) {
    wenn(!Daten || Datentyp !== 'Objekt') {
        zurückkehren;
    }
    Objekt.Schlüssel(Daten).fürJeden(Funktion(Schlüssel){
        defineReactive(Daten, Schlüssel, Daten[Schlüssel]);
    });
}

Funktion Dep() {
    dies.subs = [];
}

//Mit der Prototypeigenschaft können Sie einem Objekt Eigenschaften und Methoden hinzufügen. //Die Prototypeigenschaft ist nur für Funktionsobjekte verfügbar, insbesondere für Konstruktoren. Solange Sie ein Funktionsobjekt deklarieren, existiert der Prototyp. //Objektinstanzen haben diese Eigenschaft nicht. Dep.prototype = {                        
    addSub:Funktion(sub) {
        dies.subs.push(sub);
    },
    benachrichtigen:Funktion() {
        dies.subs.forEach(Funktion(sub) {
            sub.update(); //Jeden Abonnenten benachrichtigen, dass er nach Updates suchen soll})
    }
}
Dep.Ziel = null;

Im Code fügen wir dem Abonnenten-Dep in get ein Abonnenten-Design hinzu. Dies dient dazu, den Watcher bei seiner Initialisierung auszulösen, um festzustellen, ob ein Abonnent hinzugefügt werden muss. Auf die spezifische Implementierungsmethode gehen wir weiter unten näher ein. Wenn sich in der Set-Methode die Funktion ändert, werden alle Abonnenten benachrichtigt und die Abonnenten führen die entsprechende Aktualisierungsfunktion aus. Bisher wurde ein relativ vollständiger Observer gebildet. Als Nächstes müssen wir den Abonnenten-Watcher schreiben.

2. Implementierung des Abonnenten-Watchers

Nach unserer Vorstellung muss sich der Teilnehmer Wahcher während der Initialisierung selbst zum Teilnehmer Dep hinzufügen. Wie fügt man ihn also hinzu?

Wir wissen bereits, dass der Listener-Observer den Vorgang des Hinzufügens von Abonnenten in der Get-Funktion ausführt. Daher müssen wir nur die entsprechende Get-Funktion auslösen, wenn der Abonnenten-Watcher initialisiert wird, um den Vorgang des Hinzufügens von Abonnenten auszuführen. Wie lösen Sie also die entsprechende Get-Funktion aus? Wir müssen nur den entsprechenden Eigenschaftswert abrufen und können das entsprechende Abrufen über Object.defineProperty() auslösen.

Hier muss ein Detail beachtet werden. Wir müssen Abonnenten nur hinzufügen, wenn der Abonnent initialisiert wird. Daher müssen wir entscheiden, ob der Abonnent auf Dep.target zwischengespeichert und nach erfolgreichem Hinzufügen entfernt wird. Der Code lautet wie folgt:

Funktion Watcher(vm,exp,cb) {
    this.vm = vm; //Auf den Bereich von SelfVue zeigen this.exp = exp; //Schlüsselwert der Bindungseigenschaft this.cb = cb; //Abschluss this.value = this.get();
}

Watcher.Prototyp = {
    update:funktion() {
        dies.laufen();
    },
    ausführen:Funktion() {
        var-Wert = this.vm.data[this.exp];
        var alterWert = dieser.Wert;
        wenn(Wert !== alterWert) {
            dieser.Wert = Wert;
            this.cb.call(this.vm,Wert,alterWert);
        }
    },
    bekomme:Funktion() {
        Dep.target = this; // Zwischenspeichere dich selbst var value = this.vm.data[this.exp]; // Erzwinge die Ausführung der Get-Funktion im Listener Dep.target = null; // Gib dich selbst frei return value;
    }
}

Zu diesem Zeitpunkt müssen wir eine kleine Anpassung an defineReactive() im Listener Observer vornehmen:

Funktion defineReactive(Daten,Schlüssel,Wert) {
    beobachte(Wert);
    var dep = neues Dep();
    Object.defineProperty(Daten, Schlüssel, {
        aufzählbar: wahr,
        konfigurierbar: true,
        bekomme: Funktion() {
            if(Dep.target) { //Beurteilen, ob Abonnenten hinzugefügt werden sollen dep.addSub(Dep.target);
            }
            Rückgabewert;
        },
        setze: Funktion(neuerWert) {
            wenn (Wert === neuerWert) {
                zurückkehren;
            }
            val = neuerWert;
            console.log('property' + key + ' wurde überwacht und der aktuelle Wert ist: "' + newVal.toString() + '"');
            dep.notify(); // Wenn sich die Daten ändern, alle Abonnenten benachrichtigen}
    });
}

Bisher wurde eine vereinfachte Version von Watcher erstellt. Wir müssen nur den Abonnenten Watcher mit dem Listener Observer verknüpfen, um eine einfache bidirektionale Bindung zu erreichen. Da hier kein Befehlsparser entworfen wurde, codieren wir die Vorlagendaten fest. Angenommen, die Vorlage enthält ein Knotenelement und die ID ist „Name“, und die Bindungsvariable der bidirektionalen Bindung ist ebenfalls „Name“ und wird in zwei große doppelte Klammern eingeschlossen (was vorerst nutzlos ist). Der Vorlagencode lautet wie folgt

<Text>
    <h1 id="name">{{name}}</h1>
</body>

Wir müssen eine SelfVue-Klasse definieren, um die Verbindung zwischen Beobachter und Wächter herzustellen. Der Code lautet wie folgt:

//Observer und Watcher zuordnen Funktion SelfVue(data,el,exp) {
    diese.daten = Daten;
    beobachten(Daten);
    el.innerHTML = diese.Daten[exp];
    neuer Wächter(dieser,Ausdruck,Funktion(Wert) {
        el.innerHTML = Wert;
    });
    gib dies zurück;
}

Erstellen Sie dann ein neues SelfVue auf der Seite, um eine bidirektionale Bindung zu erreichen:

<Text>
    <h1 id="Name"{{Name}}></h1>
</body>

<script src="../js/observer.js"></script>
<script src="../js/Watcher.js"></script>
<script src="../js/SelfVue.js"></script>

<Skript>
     var ele = document.querySelector('#name');
     var selfVue = neues SelfVue({
         Name: „Hallo Welt“
     },ele,'Name');

     Fenster.setTimeout(Funktion() {
         console.log('Namenswert geändert');
         selfVue.name = "Tschüss Welt";
     },2000);
</Skript>

Wenn wir die Seite öffnen, wird „Hallo Welt“ angezeigt, was sich nach 2 Sekunden in „Tschüss Welt“ ändert. Es wird eine einfache bidirektionale Bindung erreicht.

Im Vergleich zu Vue haben wir ein Problem festgestellt. Wenn wir Attributen Werte zuweisen, lautet das Format: „selfVue.data.name = „byebye world“, und unser ideales Format lautet: „selfVue.name = „byebye world“. Wie erreichen wir also dieses Format? Wir müssen nur eine Proxy-Verarbeitung durchführen, wenn wir ein neues SelfVue erstellen, sodass der Attributzugriff auf SelfVue an den Attributzugriff auf selfVue.data delegiert wird. Das Prinzip besteht darin, Object.defineProperty() zu verwenden, um das Attribut in einer Ebene einzuschließen. Der Code lautet wie folgt:

Funktion SelfVue(Daten,el,exp) {
    var selbst = dies;
    diese.daten = Daten;
    //Die Methode Object.keys() gibt ein Array der aufzählbaren Eigenschaften eines bestimmten Objekts zurückObject.keys(data).forEach(function(key) {
        self.proxyKeys(key); //Proxy-Attribute binden });
    beobachten(Daten);
    el.innerHTML = this.data[exp]; // Initialisiere den Wert der Vorlagendaten new Watcher(this,exp,function(value) {
        el.innerHTML = Wert;
    });
    gib dies zurück;
}

SelfVue.prototype = {
    ProxyKeys:Funktion(Schlüssel) {
        var selbst = dies;
        Objekt.defineProperty(dieser,Schlüssel,{
            aufzählbar:false,
            konfigurierbar:true,
            bekomme:Funktion ProxyGetter() {
                gibt self.data[Schlüssel] zurück;
            },
            setze:Funktion ProxySetter(neuerWert) {
                self.data[Schlüssel] = neuerWert;
            } 
        });
    }
}

Auf diese Weise können wir die Vorlagendaten in die gewünschte Form ändern.

3. Implementierung des Befehlsparsers Kompilieren

In der obigen Demo zur bidirektionalen Bindung haben wir festgestellt, dass der DOM-Knoten während des gesamten Vorgangs nicht analysiert wurde, sondern ein bestimmter Knoten repariert wurde, um die Daten zu ersetzen. Daher müssen wir als Nächstes einen Parser implementieren, um ihn zu analysieren und zu binden. Anschließend müssen wir die Rolle des Parsers analysieren. Die Implementierungsschritte lauten wie folgt:

1. Vorlagenanweisungen analysieren, Vorlagendaten ersetzen und die Ansicht initialisieren

2. Binden Sie die entsprechende Aktualisierungsfunktion an den Knoten, der der Vorlagenanweisung entspricht, und initialisieren Sie den entsprechenden Abonnenten

Um die Vorlage zu analysieren, müssen wir zuerst das DOM-Element abrufen und dann die Knoten verarbeiten, die die Anweisungen für das DOM-Element enthalten. Dieser Prozess ist für die Bearbeitung des DOM-Elements umständlich, daher können wir zuerst ein Fragmentfragment erstellen und das zu analysierende DOM-Element zur Verarbeitung im Fragmentfragment speichern:

nodeToFragment:Funktion(el) {
        var fragment = document.createDocumentFragment(); //Die Methode createdocumentfragment() erstellt ein virtuelles Knotenobjekt, das alle Eigenschaften und Methoden enthält.
        var Kind = el.erstesKind;
        während(Kind) {
            // Verschiebe das Dom-Element in das Fragment fragment.appendChild(child);
            Kind = el.erstesKind;
        }
        Fragment zurückgeben;
    }

Als nächstes müssen wir alle Knoten durchlaufen und spezielle Verarbeitungen an Knoten durchführen, die Anweisungen enthalten. Hier behandeln wir zunächst den einfachsten Fall, bei dem nur Anweisungen in der Form „{{variable}}“ verarbeitet werden. Der Code lautet wie folgt:

//Durchlaufe jeden Knoten und führe eine spezielle Verarbeitung an den Knoten durch, die die relevanten Spezifikationen enthalten. compileElement:function(el) {
        var childNodes = el.childNodes; //Die childNodes-Eigenschaft gibt die untergeordnete Knotensammlung des Knotens als NodeList-Objekt zurück.
        var selbst = dies;
        //Die Methode slice() gibt die ausgewählten Elemente aus einem vorhandenen Array zurück.
        [].slice.call(childNodes).forEach(Funktion(Knoten) {
            var reg = /\{\{(.*)\}\}/;
            var text = node.textContent; //Die textContent-Eigenschaft legt den Textinhalt des angegebenen Knotens fest oder gibt ihn zurück if(self.isTextNode(node) && reg.test(text)) { //Beurteilen, ob die {{}}-Anweisung erfüllt ist//Die Methode exec() wird verwendet, um Übereinstimmungen mit regulären Ausdrücken in einer Zeichenfolge abzurufen.
                //Gibt ein Array mit den übereinstimmenden Ergebnissen zurück. Wenn keine Übereinstimmung gefunden wird, ist der Rückgabewert null.
                self.compileText(Knoten,reg.exec(Text)[1]);
            }
            wenn(node.childNodes && node.childNodes.length) {
                self.compileElement(node); //Weiter mit dem rekursiven Durchlaufen der untergeordneten Knoten}
        });
    },
    KompilierenText:Funktion(Knoten,Exp) {
        var selbst = dies;
        var initText = this.vm[exp];
        this.updateText(node,initText); // Initialisiere die initialisierten Daten in der Ansicht new Watcher(this.vm,exp,function(value) {
            self.updateText(Knoten,Wert);
        });

    },
    updateText:Funktion(Knoten,Wert) {
        node.textContent = Typ des Wertes == ‚undefiniert‘?‘‘: Wert;
    },

Nachdem Sie den äußersten Knoten erhalten haben, rufen Sie die Funktion compileElement auf, um alle untergeordneten Knoten zu beurteilen. Wenn der Knoten ein Textknoten ist und mit dem Knoten der {{}}-Formularanweisung übereinstimmt, beginnen Sie mit der Kompilierung. Der Kompilierungsprozess muss zuerst die Ansichtsdaten initialisieren, entsprechend dem oben erwähnten Schritt 1. Als Nächstes müssen Sie einen Abonnenten generieren, der die Aktualisierungsfunktion bindet, entsprechend dem oben erwähnten Schritt 2. Damit sind die drei Prozesse der Befehlsanalyse, Initialisierung und Kompilierung abgeschlossen und eine Parser-Kompilierung kann normal funktionieren.

Um den Parser Compile mit dem Listener Observer und dem Subscriber Watcher zu verknüpfen, müssen wir die Funktion der Klasse SelfVue erneut ändern:

Funktion SelfVue(Optionen) {
    var selbst = dies;
    dies.vm = dies;
    diese.Daten = Optionen.Daten;
    Object.keys(diese.Daten).forEach(Funktion(Schlüssel) {
        self.proxyKeys(key); //Proxy-Attribute binden });
    beobachten(Optionen.Daten);
    neues Kompilieren(Optionen.el,diese.vm);
    gib dies zurück;
}

Nach der Änderung müssen wir für die bidirektionale Bindung keine festen Elementwerte mehr übergeben wie bisher. Wir können beliebige Variablen für die bidirektionale Bindung benennen:

<Text>
    <div id="app">
        <h1>{{title}}</h1>
        <h2>{{name}}</h2>
        <h3>{{Inhalt}}</h3>
    </div>
</body>
<script src="../js/observer2.js"></script>
<script src="../js/Watcher1.js"></script>
<script src="../js/compile1.js"></script>
<script src="../js/index3.js"></script>


<Skript>
    var selfVue = neues SelfVue({
        el:'#app',
        Daten:{
            Titel: 'aaa',
            Name: „bbb“,
            Inhalt: „ccc“
        }
    });
    Fenster.setTimeout(Funktion() {
        selfVue.title = "ddd";
        selfVue.name = "eee";
        selfVue.content = "fff"
    },2000);
</Skript>

An diesem Punkt ist eine bidirektionale Datenbindungsfunktion grundsätzlich abgeschlossen. Der nächste Schritt besteht darin, das Parsen und Kompilieren weiterer Anweisungen zu verbessern. Wo sollten weitere Anweisungen verarbeitet werden? Die Antwort liegt auf der Hand. Fügen Sie einfach die oben erwähnte Funktion compileElement hinzu, um andere Befehlsknoten zu beurteilen, und durchlaufen Sie dann alle ihre Attribute, um zu sehen, ob es passende Befehlsattribute gibt. Wenn ja, analysieren und kompilieren Sie sie. Hier fügen wir eine v-Modell-Direktive und eine Ereignisdirektive zum Parsen und Kompilieren hinzu. Für diese Knoten verwenden wir die Kompilierungsfunktion, um sie zu parsen und zu verarbeiten:

Kompilieren:Funktion(Knoten) {
        var nodeAttrs = node.attributes; //Das Attribute-Attribut gibt den Attributsatz des angegebenen Knotens zurück, d. h. NamedNodeMap.
        var selbst = dies;
        //Die Eigenschaft Array.prototype stellt den Prototyp des Array-Konstruktors dar und ermöglicht das Hinzufügen neuer Eigenschaften und Methoden zu allen Array-Objekten.
        //Array.prototype selbst ist ein Array
        Array.prototype.forEach.call(nodeAttrs,Funktion(attr) {
            var attrName = attr.name; //Ereignismethodennamen und Präfix hinzufügen: v-on:click="onClick", dann attrName = 'v-on:click' id="app" attrname= 'id'
            wenn (self.isDirective(attrName)) {     
                var exp = attr.value; //Name und Präfix der Ereignismethode hinzufügen: v-on:click="onClick", exp = 'onClick'

                //Die Methode substring() wird verwendet, um Zeichen zwischen zwei angegebenen Indizes in einer Zeichenfolge zu extrahieren. Der Rückgabewert ist ein neuer String //dir = 'on:click'
                var dir = attrName.substring(2);  
                if(self.isEventDirective(dir)) { //Ereignisdirektive self.compileEvent(node,self.vm,exp,dir);
                }else { //v-model-Direktive self.compileModel(node,self.vm,exp,dir);
                }

                node.removeAttribute(attrName);
            }
        });
    }

Die obige Kompilierungsfunktion ist auf dem Compile-Prototyp montiert. Sie durchläuft zuerst alle Knotenattribute und bestimmt dann, ob das Attribut ein Befehlsattribut ist. Wenn ja, unterscheidet sie, um welchen Befehl es sich handelt, und führt dann die entsprechende Verarbeitung durch.

Zum Schluss ändern wir SelfVue noch einmal, damit das Format mehr dem von Vue ähnelt:

Funktion SelfVue(Optionen) {
    var selbst = dies;
    diese.Daten = Optionen.Daten;
    diese.Methoden = Optionen.Methoden;
    Object.keys(diese.Daten).forEach(Funktion(Schlüssel) {
        self.proxyKeys(Schlüssel);    
    });
    beobachten(Optionen.Daten);
    neues Kompilieren(Optionen.el,dies);
    Optionen.mounted.call(dies);
}

Probieren Sie es aus:

<Text>
    <div id="app">
            <h2>{{title}}</h2>
            <Eingabe v-Modell="Name">
            <h1>{{name}}</h1>
            <button v-on:click="clickMe">klick mich!</button>
    </div>
</body>

<script src="../js/observer3.js"></script>
<script src="../js/Watcher1.js"></script>
<script src="../js/compile2.js"></script>
<script src="../js/index4.js"></script>
<Skript>
    neues SelfVue({
        el: '#app',
        Daten: {
            Titel: 'Hallo Welt',
            Name: "canfoo"
        },
        Methoden: {
            klickMich: Funktion () {
                this.title = "Hallo Welt";
            }
        },
        montiert: Funktion () {
            fenster.setTimeout(() => {
                dieser.Titel = 'Hallo';
            }, 1000);
        }
    });
</Skript>

Die Wirkung ist wie folgt:

Bisher war unsere vereinfachte Demo erfolgreich. Durch das obige Beispiel können wir einige Mechanismen von Vue besser verstehen, z. B. bidirektionale Bindung, deklaratives Rendering usw.

Das Obige ist der detaillierte Inhalt eines vertieften Verständnisses der Verwendung von Vue. Weitere Informationen zum vertieften Verständnis von Vue finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Zusammenfassung der Ereignisbehandlung im Vue.js-Frontend-Framework
  • Nicht-technischer Praxisnachweis des iView UI-Frameworks basierend auf Vue.js (empfohlen)
  • Vue.js Universal Application Framework - Nuxt.js – Erste Schritte
  • Die Fallstricke beim Erlernen von Vue.js
  • Detaillierte Einführungshinweise zu Vue
  • So verstehen Sie die Dateninteraktion und -anzeige im Vue-Frontend und -Backend
  • Emberjs-Methode zum Herunterladen von Dateien über Axios
  • Entdecken Sie Emberjs, um eine einfache Todo-Anwendung zu erstellen
  • Detaillierter Vergleich von Ember.js und Vue.js

<<:  Deinstallieren Sie die MySQL-Datenbank im Windows-System vollständig, um MySQL neu zu installieren

>>:  Installation und Bereitstellung des Linux-Tools Nethogs zur Überwachung der Netzwerkbandbreite nach Prozess

Artikel empfehlen

HTML-Sprachenzyklopädie

123WORDPRESS.COM--HTML超文本标记语言速查手册<!-- --> !D...

Beispiel für die Implementierung eines gestrichelten Rahmens mit html2canvas

html2canvas ist eine Bibliothek, die Canvas aus H...

Verwenden Sie CSS, um ein Datei-Upload-Muster zu zeichnen

Wenn Sie es wären, wie würden Sie es erreichen, w...

Vue.js implementiert Kalenderfunktion

In diesem Artikelbeispiel wird der spezifische Co...

Spezifische Verwendung der MySQL-Vorbereitungsvorverarbeitung

Inhaltsverzeichnis 1. Vorverarbeitung 2. Vorbehan...

Aktivieren und Konfigurieren des MySQL-Protokolls für langsame Abfragen

Einführung Das MySQL-Protokoll für langsame Abfra...

Canvas zeichnet Rubbellos-Effekt

In diesem Artikel wird der spezifische Code zum Z...