Der Quellcode zeigt, warum Vue2 Daten und Methoden direkt abrufen kann

Der Quellcode zeigt, warum Vue2 Daten und Methoden direkt abrufen kann

1. Beispiel: Hiermit können Daten und Methoden direkt abgerufen werden

Beispiel:

const vm = neuer Vue({
    Daten: {
        Name: "Ich bin Ruochuan",
    },
    Methoden: {
        sagName(){
            konsole.log(dieser.name);
        }
    },
});
console.log(vm.name); // Ich bin Ruochuanconsole.log(vm.sayName()); // Ich bin Ruochuan

Auf diese Weise kann ich ausgeben, dass ich Ruochuan bin. Neugierige werden sich fragen, warum hierauf direkt zugegriffen werden kann.

Warum also kann this.xxx die Daten in data und methods abrufen?
Wie können wir einen ähnlichen Effekt wie Vue erzielen, indem wir die Funktionen konstruieren, die wir selbst schreiben?

Funktion Person(Optionen){

}

const p = neue Person({
    Daten: {
        Name: „Kleinanzeigen“
    },
    Methoden: {
        sagName(){
            konsole.log(dieser.name);
        }
    }
});

Konsole.log(p.name);
// undefiniert
console.log(p.sayName());
// Nicht abgefangener TypeError: p.sayName ist keine Funktion

Wenn Sie es wären, wie würden Sie es erreichen? Lassen Sie uns anhand dieser Fragen den Vue2-Quellcode debuggen und lernen.

2. Bereiten Sie die Umgebung vor und debuggen Sie den Quellcode, um mehr herauszufinden

Sie können lokal einen neuen Ordner examples und eine neue Datei index.html erstellen.
Fügen Sie das folgende js in <body></body> .

<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<Skript>
    const vm = neuer Vue({
        Daten: {
            Name: "Ich bin Ruochuan",
        },
        Methoden: {
            sagName(){
                konsole.log(dieser.name);
            }
        },
    });
    Konsole.log(VM-Name);
    Konsole.log(vm.sayName());
</Skript>

Installieren Sie dann npm i -g http-server global, um den Dienst zu starten.

npm i -g http-server
CD-Beispiele
http-Server.
// Falls der Port belegt ist, können Sie den Port auch http-server -p 8081 angeben.

Auf diese Weise können Sie die gerade geschriebene Seite的index.html unter http://localhost:8080/ öffnen.

Debuggen: Öffnen Sie das Debuggen in F12, Quellcode-Fenster, im Beispiel const vm = new Vue({, setzen Sie einen Haltepunkt.

Drücken Sie nach dem Aktualisieren der Seite F11, um die Funktion aufzurufen. Der Haltepunkt gelangt in den Vue-Konstruktor.

2.1 Vue-Konstruktor

Funktion Vue (Optionen) {
    wenn (!(diese Instanz von Vue)
    ) {
        warnen('Vue ist ein Konstruktor und sollte mit dem Schlüsselwort ‚new‘ aufgerufen werden');
    }
    this._init(Optionen);
}
// Initialisieren initMixin(Vue);
Zustandsmixin(Vue);
EreignisseMixin(Vue);
LebenszyklusMixin(Vue);
: renderMixin(Vue);

Erwähnenswert ist Folgendes :if (!(this instanceof Vue)){} bestimmt, ob der Konstruktor mit dem neuen Schlüsselwort aufgerufen wird.
Normalerweise würden wir nicht daran denken, dies zu schreiben.
Natürlich können Sie new auch innerhalb Ihrer eigenen Funktion aufrufen, indem Sie sich die Quellcodebibliothek ansehen. Aber im Allgemeinen benötigt ein vue -Projekt nur einmal new Vue() , es ist also nicht notwendig.
Der jQuery-Quellcode ist intern neu, das heißt, es entsteht für den Benutzer keine new Struktur.

jQuery = Funktion (Selektor, Kontext) {
  //Gibt das neue Objekt zurück return new jQuery.fn.init( selector, context );
};

Weil jQuery oft aufgerufen wird.
Tatsächlich kann jQuery auch new sein. Dies hat den gleichen Effekt, als ob Sie „new“ nicht verwenden würden.

Debuggen: Setzen Sie weiterhin einen Haltepunkt bei this._init(Optionen) und drücken Sie F11, um die Funktion aufzurufen.

2.2 _init Initialisierungsfunktion

Nach dem Aufrufen der Funktion _init ist diese Funktion relativ lang und führt viele Dinge aus. Wir vermuten, dass sich die Implementierung in Bezug auf data und methods in initState(vm) befindet.

// Code wurde gelöscht Funktion initMixin (Vue) {
    Vue.prototype._init = Funktion (Optionen) {
      var vm = dies;
      // eine UID
      vm._uid = uid$3++;

      // ein Flag, um zu verhindern, dass dies beobachtet wird
      vm._isVue = true;
      // Merge-Optionen
      if (Optionen && Optionen._istKomponente) {
        // Instanziierung interner Komponenten optimieren
        // da das Zusammenführen dynamischer Optionen ziemlich langsam ist und keine der
        // Interne Komponentenoptionen erfordern eine besondere Behandlung.
        initInternalComponent(vm, Optionen);
      } anders {
        vm.$optionen = mergeOptionen(
          Konstruktoroptionen auflösen(vm.constructor),
          Optionen || {},
          vm
        );
      }

      // wahres Ich enthüllen
      vm._self = vm;
      initLifecycle(vm);
      : InitEvents(vm);
      initRender(vm);
      callHook(vm, 'vorErstellen');
      initInjections(vm); // Injektionen vor Daten/Eigenschaften auflösen
      // Initialisierungsstatus initState(vm);
      initProvide(vm); // provide nach data/props auflösen
      callHook(vm, 'erstellt');
    };
}

Debuggen: Als Nächstes setzen wir einen Haltepunkt in initState(vm) . Drücken Sie F8, um direkt zu diesem Haltepunkt zu springen, und drücken Sie dann F11, um die Funktion initState aufzurufen.

2.3 initState Initialisierungsstatus

Gemessen am Funktionsnamen sind die Hauptfunktionen dieser Funktion:

  • Initialisieren von props
  • methods
  • Überwachungsdaten
  • computed initialisieren
  • watch initialisieren
Funktion initState (vm) {
    vm._watchers = [];
    var opts = vm.$optionen;
    wenn (opts.props) { initProps(vm, opts.props); }
    // Es wurden Methoden übergeben, Initialisierungsmethode if (opts.methods) { initMethods(vm, opts.methods); }
    //Daten eingeben, Daten initialisieren
    wenn (opts.data) {
      initData(vm);
    } anders {
      beobachten(vm._data = {}, true /* asRootData */);
    }
    wenn (opts.computed) { initComputed(vm, opts.computed); }
    wenn (opts.watch && opts.watch !== nativeWatch) {
      initWatch(vm, opts.watch);
    }
}

Wir konzentrieren uns auf methods und dann auf data .

Debuggen: Setzen Sie einen Haltepunkt bei initMethods und initData(vm) . Nachdem Sie die Funktion initMethods gelesen haben, können Sie direkt F8 drücken, um zur Funktion initData(vm) zurückzukehren. Drücken Sie weiterhin F11 und geben Sie zuerst die Funktion initMethods ein.

2.4 Initialisierungsmethode initMethods

Funktion initMethods (vm, Methoden) {
    var props = vm.$options.props;
    für (var Schlüssel in Methoden) {
      {
        if (Typ der Methoden[Schlüssel] !== 'Funktion') {
          warnen(
            "Methode \"" + Schlüssel + "\" hat den Typ \"" + (Typ der Methoden[Schlüssel]) + "\" in der Komponentendefinition. " +
            "Haben Sie die Funktion richtig referenziert?",
            vm
          );
        }
        wenn (Requisiten und hasOwn (Requisiten, Schlüssel)) {
          warnen(
            ("Methode \"" + Taste + "\" wurde bereits als Eigenschaft definiert."),
            vm
          );
        }
        if ((Schlüssel in vm) && isReserved(Schlüssel)) {
          warnen(
            "Methode \"" + Schlüssel + "\" steht im Konflikt mit einer vorhandenen Vue-Instanzmethode. " +
            „Vermeiden Sie die Definition von Komponentenmethoden, die mit _ oder $ beginnen.“
          );
        }
      }
      vm[Schlüssel] = Typ der Methoden[Schlüssel] !== 'Funktion' ? noop : binden(Methoden[Schlüssel], vm);
    }
}

Die Funktion initMethods enthält hauptsächlich einige Urteile.

  • Bestimmen Sie, ob jedes Element in methods eine Funktion ist. Wenn nicht, geben Sie eine Warnung aus.
  • Bestimmen Sie, ob jedes Element in methods mit Eigenschaften in Konflikt steht. Wenn ja, geben Sie eine Warnung aus.
  • Bestimmen Sie, ob jedes Element in methods bereits auf der neuen Vue-Instanz-VM vorhanden ist und ob der Methodenname mit dem reservierten _ $ beginnt (bezieht sich im Allgemeinen auf die interne Variablenkennung in JS). Wenn ja, warnen Sie.

Abgesehen von diesen Beurteilungen können wir sehen, dass die Funktion initMethods tatsächlich das übergebene methods durchläuft und bind Bindungsfunktion Bind verwendet, um auf vm zu verweisen, das das Instanzobjekt von new Vue ist.

Deshalb können wir hierüber direkt auf die Funktionen in methods zugreifen.

Wir können die Maus auf die bind Variable bewegen und die Alt-Taste drücken, um zu sehen, wo die Funktion definiert ist. Dies ist Zeile 218. Klicken Sie, um hierher zu springen und die Implementierung von Bind anzuzeigen.

2.4.1 bind gibt eine Funktion zurück und ändert, worauf diese zeigt

Funktion polyfillBind (fn, ctx) {
    Funktion boundFn (a) {
      var l = Argumente.Länge;
      Rückkehr l
        ? l > 1
          ? fn.apply(ctx, Argumente)
          : fn.call(ctx, a)
        : fn.aufruf(ctx)
    }

    boundFn._length = fn.länge;
    Rückgabewert boundFn
}

Funktion nativeBind (fn, ctx) {
  gibt fn.bind(ctx) zurück
}

var bind = Funktion.prototype.bind
  ? nativeBind
  :polyfillBind;

Einfach ausgedrückt ist es mit der alten Version kompatibel, die die native bind -Funktion nicht unterstützt. Gleichzeitig ist es mit dem Schreiben von Methoden kompatibel, beurteilt die Anzahl der Parameter und verwendet call und apply um es zu implementieren. Es wird gesagt, dass dies auf Leistungsprobleme zurückzuführen ist.
Wenn Sie mit der Verwendung und Implementierung von call , apply “ und bind nicht vertraut sind, können Sie call und „ apply -Methoden von JS simulieren und implementieren?

Debuggen: Drücken Sie nach dem Lesen der Funktion initMethods F8, um zum oben erwähnten Haltepunkt der Funktion initData(vm) zurückzukehren.

2.5 initData Daten initialisieren

  • initData trifft auch einige Beurteilungen. Die wichtigsten Dinge, die erledigt werden, sind die folgenden:
  • Weisen Sie _data zunächst einen Wert zur späteren Verwendung zu.
  • data handelt es sich nicht um ein Objekt und es wird eine Warnung ausgegeben.
  • data , die jeweils:
  • Bei Konflikten mit methods wird eine Warnung ausgegeben.
  • Wenn ein Konflikt mit props vorliegt, wird eine Warnung ausgegeben.
  • Es handelt sich nicht um ein internes, privates, reserviertes Attribut. Es fungiert als Proxy für _data .
  • Überwachen Sie abschließend data und sorgen Sie dafür, dass sie reaktionsfähig sind.
Funktion initData (vm) {
    var Daten = vm.$Optionen.Daten;
    Daten = vm._Daten = Datentyp === 'Funktion'
      ? getData(Daten, vm)
      : Daten || {};
    wenn (!isPlainObject(data)) {
      Daten = {};
      warnen(
        'Datenfunktionen sollten ein Objekt zurückgeben:\n' +
        „https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function“,
        vm
      );
    }
    // Proxy-Daten auf der Instanz
    var keys = Objekt.keys(Daten);
    var props = vm.$options.props;
    var Methoden = vm.$options.methods;
    var i = Schlüssel.Länge;
    während (i--) {
      var Schlüssel = Schlüssel[i];
      {
        if (Methoden && hasOwn(Methoden, Schlüssel)) {
          warnen(
            ("Methode \"" + Schlüssel + "\" wurde bereits als Dateneigenschaft definiert."),
            vm
          );
        }
      }
      wenn (Requisiten und hasOwn (Requisiten, Schlüssel)) {
        warnen(
          "Die Dateneigenschaft \"" + Schlüssel + "\" ist bereits als Prop deklariert. " +
          "Stattdessen den Standardwert der Eigenschaft verwenden.",
          vm
        );
      } sonst wenn (!isReserved(Schlüssel)) {
        Proxy (vm, "_data", Schlüssel);
      }
    }
    // Daten beobachten
    beobachten(Daten, wahr /* asRootData */);
}

2.5.1 Daten abrufen

Wenn es sich um eine Funktion handelt, rufen Sie die Funktion auf und führen Sie sie aus, um das Objekt zu erhalten.

Funktion getData (Daten, vm) {
    // #7573 Deaktivierung der Dep-Sammlung beim Aufrufen von Datengettern
    Ziel drücken();
    versuchen {
      gibt Daten zurück.Aufruf(vm, vm)
    } fangen (e) {
      Fehlerbehandlung(e, vm, "data()");
      zurückkehren {}
    Endlich
      popZiel();
    }
}

2.5.2 Proxy

Tatsächlich wird Object.defineProperty verwendet, um das Objekt zu definieren. Der Zweck hier ist: this.xxx dient zum Zugriff auf this._data.xxx。

/**
   * Führen Sie keine Operation aus.
   * Stubbing von Argumenten, um Flow zufriedenzustellen, ohne nutzlosen transpilierten Code zu hinterlassen
   * mit ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
   */
Funktion noop (a, b, c) {}
var sharedPropertyDefinition = {
    aufzählbar: wahr,
    konfigurierbar: true,
    bekomme: noop,
    gesetzt: noop
};

Funktion Proxy (Ziel, Quellschlüssel, Schlüssel) {
    sharedPropertyDefinition.get = Funktion ProxyGetter () {
      gib dies zurück[sourceKey][key]
    };
    sharedPropertyDefinition.set = Funktion ProxySetter (Wert) {
      dies[Quellschlüssel][Schlüssel] = Wert;
    };
    Object.defineProperty(Ziel, Schlüssel, gemeinsam genutztePropertyDefinition);
}

2.5.3 Object.defineProperty definiert Objekteigenschaften

  • Object.defineProperty ist eine sehr wichtige API. Es gibt auch eine API zum Definieren mehrerer Eigenschaften: Object.defineProperties(obj, props) (ES5)
  • Object.defineProperty beinhaltet einige wichtige Wissenspunkte und wird oft in Interviews getestet.
  • value – Der beim Versuch, die Eigenschaft abzurufen, zurückzugebende Wert.
  • writable – ob die Eigenschaft beschreibbar ist.
  • enumerable – ob die Eigenschaft in einer For-In-Schleife aufgezählt wird.
  • configurable - ob die Eigenschaft gelöscht werden kann.
  • set() — Die Funktion, die von Aktualisierungsvorgängen für diese Eigenschaft aufgerufen wird.
  • get() – Die aufgerufene Funktion, um den Wert einer Eigenschaft abzurufen.

2.6 Einige im Artikel vorkommende Funktionen werden nun endlich einheitlich erklärt

2.6.1 hasOwn Handelt es sich um eine Eigenschaft, die dem Objekt selbst gehört?

Drücken Sie im Debugmodus die Alt-Taste und bewegen Sie die Maus über den Methodennamen, um zu sehen, wo die Funktion definiert ist. Klicken Sie, um zu springen.
/**
   * Prüfen, ob ein Objekt die Eigenschaft besitzt.
   */
var hasOwnProperty = Object.prototype.hasOwnProperty;
Funktion hasOwn (Objekt, Schlüssel) {
  returniere hasOwnProperty.call(Objekt, Schlüssel)
}

hasOwn({ a: undefiniert }, 'a') // wahr
hasOwn({}, 'a') // falsch
hasOwn({}, 'hasOwnProperty') // falsch
hasOwn({}, 'toString') // falsch
// Es ist eine Eigenschaft der Eigenschaft selbst und wird nicht nach oben durch die Prototypenkette gesucht.

2.6.2 isReserved Ob es sich um eine interne private reservierte Zeichenfolge handelt, die mit $ und _ beginnt

/**
   * Prüfen Sie, ob eine Zeichenfolge mit $ oder _ beginnt
   */
Funktion ist reserviert (str) {
  var c = (str + '').charCodeAt(0);
  Rückgabe c === 0x24 || c === 0x5F
}
istReserviert('_data'); // wahr
istReserviert('$optionen'); // wahr
isReserved('data'); // falsch
isReserved('Optionen'); // falsch

3. Abschließend wird eine vereinfachte Version mit über 60 Zeilen Code implementiert

Funktion noop (a, b, c) {}
var sharedPropertyDefinition = {
    aufzählbar: wahr,
    konfigurierbar: true,
    bekomme: noop,
    gesetzt: noop
};
Funktion Proxy (Ziel, Quellschlüssel, Schlüssel) {
    sharedPropertyDefinition.get = Funktion ProxyGetter () {
      gib dies zurück[sourceKey][key]
    };
    sharedPropertyDefinition.set = Funktion ProxySetter (Wert) {
      dies[Quellschlüssel][Schlüssel] = Wert;
    };
    Object.defineProperty(Ziel, Schlüssel, gemeinsam genutztePropertyDefinition);
}
Funktion initData(vm){
  const data = vm._data = vm.$options.data;
  const keys = Objekt.keys(Daten);
  var i = Schlüssel.Länge;
  während (i--) {
    var Schlüssel = Schlüssel[i];
    Proxy (vm, „_data“, Schlüssel);
  }
}
Funktion initMethods(vm, Methoden){
  für (var Schlüssel in Methoden) {
    vm[Schlüssel] = Typ der Methoden[Schlüssel] !== 'Funktion' ? noop : Methoden[Schlüssel].bind(vm);
  } 
}

Funktion Person(Optionen){
  lass vm = dies;
  vm.$options = Optionen;
  var opts = vm.$optionen;
  wenn(opts.data){
    initData(vm);
  }
  wenn(opts.methods){
    initMethods(vm, opts.methods)
  }
}

const p = neue Person({
    Daten: {
        Name: „Kleinanzeigen“
    },
    Methoden: {
        sagName(){
            konsole.log(dieser.name);
        }
    }
});

Konsole.log(p.name);
// Vor der Implementierung: undefiniert
// 'Kleinanzeigen'
console.log(p.sayName());
// Vor der Implementierung: Nicht abgefangener TypeError: p.sayName ist keine Funktion
// 'Kleinanzeigen'

4. Fazit

Die in diesem Artikel behandelten Grundkenntnisse sind im Wesentlichen folgende:

  • Konstruktor
  • this deutet auf
  • call , bind , apply
  • Object.defineProperty

Dieser Artikel soll Fragen beantworten, die von Lesern des Quellcodes gestellt werden. Er beschreibt ausführlich, wie man den Vue-Quellcode debuggt, um die Antwort zu finden.
Beantwortung der Fragen am Anfang des Artikels:
Der Grund für den direkten Zugriff auf die Funktion in methods über this ist: weil die Methode in methods this über Bind als new Vue Instanz (VM) angibt.
Der Grund für den direkten Zugriff auf die Daten in Daten über this besteht darin, dass die Eigenschaften in den Daten letztendlich im _data-Objekt auf der neuen Vue-Instanz (VM) gespeichert werden. Der Zugriff auf this.xxx ist der Zugriff auf this._data.xxx。 nach Object.defineProperty -Proxy.
Der Vorteil dieses Vue-Designs besteht darin, dass es leicht zu erhalten ist. Es gibt auch einen Nachteil: props , methods und Daten sind anfällig für Konflikte.
Der Artikel ist insgesamt nicht schwierig, den Lesern wird jedoch dringend empfohlen, die Fehlerbehebung selbst durchzuführen. Nach dem Debuggen stellen Sie möglicherweise fest, dass der Vue-Quellcode nicht so schwierig ist, wie Sie dachten, und Sie können einen Teil davon verstehen.
Inspiration: Wenn wir mit gängigen Technologien, Frameworks oder Bibliotheken arbeiten, sollten wir neugierig bleiben und mehr über die internen Prinzipien nachdenken. Kennen Sie die Fakten und die Gründe dahinter. Sie können viele Menschen bei weitem übertreffen.
Sie fragen sich vielleicht, warum das Schlüsselwort this in der Vorlagensyntax weggelassen werden kann. Tatsächlich wird with beim Kompilieren interner Vorlagen verwendet. Leser mit mehr Energie können dieses Prinzip erkunden.

Dies ist das Ende dieses Artikels über den Quellcode, der erklärt, warum Vue2 Daten und methods direkt abrufen kann. Weitere relevante Inhalte zum direkten Abrufen von Daten und Methoden durch Vue2 finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder durchsuchen Sie die folgenden verwandten Artikel weiter. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung zur dynamischen Aktualisierung der Tabelle mithilfe von vue2.0 in Kombination mit dem DataTable-Plugin
  • Vergleich der Vorteile von vue3 und vue2
  • Beispiele für die Verwendung von „Provide“ und „Inject“ in Vue2.0/3.0
  • Vue2.x konfiguriert Routing-Navigations-Guards, um die Benutzeranmeldung und -beendigung zu implementieren.
  • Detaillierte Untersuchung von vue2.x - Erklärung der h-Funktion
  • Vue2.x-Reaktionsfähigkeit – einfache Erklärung und Beispiele
  • Zusammenfassung der Vorteile von Vue3 gegenüber Vue2
  • Vue2-Implementierungen bieten Injection für Reaktionsfähigkeit
  • Eine kurze Analyse des Reaktionsprinzips und der Unterschiede von Vue2.0/3.0
  • vue2.x-Konfiguration von vue.config.js zur Projektoptimierung
  • Beispiel für handschriftliches Vue2.0-Daten-Hijacking
  • Vue2.x - Beispiel für die Verwendung von Anti-Shake und Throttling

<<:  Detaillierte Erklärung der reinen SQL-Anweisungsmethode basierend auf JPQL

>>:  MySQL-Gruppe durch Gruppieren mehrerer Felder

Artikel empfehlen

WeChat-Applet + ECharts zur Realisierung eines dynamischen Aktualisierungsprozesses

Vorwort Kürzlich stieß ich auf eine Anforderung, ...

Einfache Verwendung des Vue-Busses

Einfache Verwendung des Vue-Busses Beschreibung d...

js Canvas realisiert zufällige Partikeleffekte

In diesem Artikelbeispiel wird der spezifische Co...

MySQL-Optimierungszusammenfassung – Gesamtzahl der Abfrageeinträge

1. COUNT(*) und COUNT(COL) COUNT(*) führt normale...

Einige Hinweise zur MySQL-Self-Join-Deduplizierung

Lassen Sie mich kurz das Funktionsszenario erklär...

MySQL Best Practices: Grundlegende Arten von Partitionstabellen

Übersicht über partitionierte MySQL-Tabellen Da M...

Detaillierte Erläuterung der Linux-Textverarbeitungstools

1. Zählen Sie die Anzahl der Benutzer, deren Stan...

MySQL-Abfrage gibt an, dass das Feld keine Zahl und kein Komma SQL ist

Grundlegende SQL-Anweisungen MySQL-Abfrageanweisu...

So beheben Sie den MySQL-FEHLER 1045 (28000) - Zugriff wegen Benutzer verweigert

Problembeschreibung (die folgende Diskussion besc...

JS-Dekorationsmuster und TypeScript-Dekoratoren

Inhaltsverzeichnis Einführung in das Decorator-Mu...

Lösen Sie das Problem des unzureichenden Docker-Festplattenspeichers

Nachdem der Server, auf dem sich Docker befindet,...