So starten Sie schnell mit VUE3.0: Geben Sie ein und lernen Sie, dass
Nest einen Modulmechanismus bereitstellt, der durch die Definition von Anbietern, Importen, Exporten und Anbieterkonstruktoren im Moduldekorator abgeschlossen wird, und die Entwicklung der gesamten Anwendung wird durch organisiert Modulbaum. Es ist absolut kein Problem, eine Anwendung gemäß den Konventionen des Frameworks selbst direkt zu starten. Allerdings habe ich das Gefühl, dass mir ein klareres und systematischeres Verständnis der vom Framework deklarierten Abhängigkeitsinjektion, Kontrollumkehr, Module, Anbieter, Metadaten, zugehörige Dekoratoren usw. fehlt.
- Warum ist eine Umkehrung der Kontrolle erforderlich?
- Was ist Abhängigkeitsinjektion?
- Was macht der Dekorateur?
- Was sind die Implementierungsprinzipien von Anbietern, Importen und Exporten in Modulen (@Module)?
Ich scheine in der Lage zu sein, es zu verstehen und zu schätzen, aber lassen Sie es mich von Anfang an klar erklären, ich kann es nicht klar erklären. Also habe ich etwas recherchiert und diesen Artikel verfasst. Von nun an fangen wir bei Null an und geben den Haupttext ein.
1.1 Express, Koa
Der Entwicklungsprozess einer Sprache und ihrer technischen Gemeinschaft muss sich von unten nach oben schrittweise bereichern und entwickeln, genau wie der Prozess, bei dem Baumwurzeln langsam zu Zweigen und dann voller Blätter heranwachsen. Früher erschienen in Nodejs grundlegende Webservice-Frameworks wie Express und Koa. Kann eine sehr grundlegende Servicefunktion bereitstellen. Basierend auf einem solchen Framework wurde in der Community eine große Anzahl von Middleware und Plug-Ins geboren, die umfangreichere Dienste für das Framework bereitstellten. Wir müssen Anwendungsabhängigkeiten organisieren und selbst Anwendungsgerüste erstellen, was flexibel und umständlich ist und auch eine gewisse Arbeitsbelastung erfordert.
Später in der Entwicklung wurden einige Frameworks mit effizienterer Produktion und einheitlicheren Regeln geboren, die eine neuere Phase einleiteten.
1.2 EggJs und Nestjs
Um eine bessere Anpassungsfähigkeit an schnelle Produktionsanwendungen zu gewährleisten, Standards zu vereinheitlichen und sie sofort verfügbar zu machen, wurden Frameworks wie EggJs, NestJs und Midway entwickelt. Diese Art von Framework abstrahiert die Implementierung einer Anwendung in einen universellen und erweiterbaren Prozess, indem es den zugrunde liegenden Lebenszyklus implementiert. Wir müssen nur der vom Framework bereitgestellten Konfigurationsmethode folgen, um die Anwendung einfacher zu implementieren. Das Framework implementiert die Prozesssteuerung des Programms und wir müssen unsere Teile nur an der entsprechenden Stelle zusammenbauen. Dies ähnelt eher einer Fließbandarbeit, und es werden viele Implementierungskosten eingespart.
1.3 Zusammenfassung
Die oben genannten zwei Phasen sind nur eine Vorahnung, dass das Upgrade des Frameworks die Produktionseffizienz verbessert, indem einige Designideen und -muster in Nest eingeführt werden. Abhängigkeitsinjektion und die Konzepte der Metaprogrammierung, lassen Sie uns weiter unten darüber sprechen.
2.1 Abhängigkeitsinjektion
Eine Anwendung besteht eigentlich aus vielen abstrakten Klassen, die alle Funktionen der Anwendung realisieren, indem sie sich gegenseitig aufrufen. Mit zunehmender Komplexität des Anwendungscodes und der Funktionen wird die Wartung des Projekts definitiv immer schwieriger, da es immer mehr Klassen gibt und die Beziehungen zwischen ihnen immer komplexer werden.
Wenn wir beispielsweise Koa zum Entwickeln unserer Anwendung verwenden, implementiert Koa selbst hauptsächlich eine Reihe grundlegender Webdienstfunktionen. Bei der Implementierung der Anwendung definieren wir viele Klassen, die Instanziierungsmethoden und die gegenseitigen Abhängigkeiten dieser Klassen können von uns in der Codelogik frei organisiert und gesteuert werden. Die Instanziierung jeder Klasse erfolgt manuell neu durch uns, und wir können steuern, ob eine Klasse nur einmal instanziiert und dann gemeinsam genutzt wird oder ob sie jedes Mal instanziiert wird. Die folgende Klasse B hängt von A ab. Jedes Mal, wenn B instanziiert wird, wird A einmal instanziiert, sodass A für jede Instanz B eine Instanz ist, die nicht gemeinsam genutzt wird.
Klasse A{} // B Klasse B{ Konstruktor(){ this.a = neues A(); } }
Das folgende C ist die erhaltene externe Instanz, sodass mehrere C-Instanzen die app.a-Instanz gemeinsam nutzen.
Klasse A{} // C const app = {}; app.a = neues A(); Klasse C{ Konstruktor(){ this.a = app.a; } }
Das folgende D wird über den Konstruktorparameter übergeben. Sie können jedes Mal eine nicht gemeinsam genutzte Instanz übergeben, oder Sie können die gemeinsam genutzte app.a-Instanz übergeben (D und F teilen sich app.a), und zwar aufgrund der Art und Weise Es ist jetzt ein Parameter. Übergeben, ich kann auch eine X-Klasseninstanz übergeben.
Klasse A{} Klasse X{} //D const app = {}; app.a = neues A(); Klasse D{ Konstruktor(a){ this.a = a; } } Klasse F{ Konstruktor(a){ this.a = a; } } neues D(app.a) neues F(app.a)
neues
D(neu
Die Injektion durch den Konstruktor (Wertübergabe) ist nur eine Implementierungsmethode. Sie kann auch durch Implementierung des Methodenaufrufs set oder einer anderen Methode übergeben werden, sofern eine externe Abhängigkeit an die interne übergeben werden kann. Es ist wirklich so einfach.
Klasse A{} //D Klasse D{ setDep(a){ this.a = a; } } const d = neues D() d.setDep(new A())
2.2 Alles in Abhängigkeitsinjektion?
Im Verlauf der Iteration scheint es, dass sich die Abhängigkeiten von B je nach unterschiedlichen Voraussetzungen ändern. Beispielsweise muss eine Vorbedingung „ this.a
in der Instanz von A übergeben werden, und eine Vorbedingung zwei this.a
muss in der Instanz von X übergeben werden. Zu diesem Zeitpunkt beginnen wir mit der eigentlichen Abstraktion. Wir werden es in eine Abhängigkeitsinjektionsmethode wie D oben umwandeln.
In den frühen Tagen, als wir die Anwendung implementierten, implementierten wir die Schreibmethode der Klassen B und C, solange sie den damaligen Anforderungen entsprach. Nachdem das Projekt mehrere Jahre lang iteriert hatte, war dies kein Problem des Codes nicht unbedingt berührt werden. Wenn wir eine spätere Erweiterung in Betracht ziehen, wirkt sich dies auf die Entwicklungseffizienz aus und ist möglicherweise nicht sinnvoll. Daher stoßen wir meistens auf Szenarien, die eine Abstraktion erfordern und dann einen Teil des Codes abstrakt transformieren.
// Klasse B{ vor der Transformation Konstruktor(){ this.a = neues A(); } } neues B() //Klasse D{ nach der Transformation Konstruktor(a){ this.a = a; } } neues D(neues A())neues D
(
neueImplementierungskosten.
Dieses Beispiel wird hier gegeben, um dies in einem Entwicklungsmodell ohne Einschränkungen oder Vorschriften zu veranschaulichen. Wir können Code frei schreiben, um Abhängigkeiten zwischen verschiedenen Klassen zu steuern. In einer völlig offenen Umgebung ist es sehr frei. Dies ist eine primitive Ära der Brandrodung. Da es kein festes Code-Entwicklungsmodell und keinen übergeordneten Aktionsplan gibt, wird die Abhängigkeitsbeziehung mit zunehmendem Code sehr unterschiedlich, da verschiedene Entwickler eingreifen oder derselbe Entwickler zu unterschiedlichen Zeiten Code schreibt. Offensichtlich kann die gemeinsam genutzte Instanz mehrmals instanziiert werden , Speicher verschwenden. Aus dem Code ist es schwierig, eine vollständige Abhängigkeitsstruktur zu erkennen, und die Wartung des Codes kann sehr schwierig werden.
Dann schreiben wir jedes Mal, wenn wir eine Klasse definieren, sie gemäß der Abhängigkeitsinjektionsmethode und schreiben sie wie D. Dann wird der Abstraktionsprozess von C und B vorangetrieben, was die spätere Erweiterung bequemer macht und die Transformationskosten senkt. Das nennt man also All in 依赖注入
, das heißt, alle unsere Abhängigkeiten werden durch Abhängigkeitsinjektion implementiert.
Allerdings werden die frühen Implementierungskosten wieder hoch und es ist schwierig, Einheit und Beständigkeit in der Teamzusammenarbeit zu erreichen. Am Ende kann die Implementierung auch als Überentwurf definiert werden, da die zusätzlichen Implementierungskosten möglicherweise nicht anfallen unbedingt Vorteile bringen.
2.3 Umkehrung der Kontrolle
Nachdem wir uns nun auf die einheitliche Verwendung der Abhängigkeitsinjektion geeinigt haben, können wir einen Controller auf unterster Ebene durch die zugrunde liegende Kapselung des Frameworks implementieren und uns auf eine Abhängigkeitskonfigurationsregel einigen. Der Controller steuert den Instanziierungsprozess basierend auf dem Die von uns definierte Abhängigkeitskonfiguration und die gemeinsame Nutzung von Abhängigkeiten helfen uns bei der Klassenverwaltung. Dieses Entwurfsmuster wird als Umkehrung der Kontrolle bezeichnet.
Wenn man es zum ersten Mal hört, kann es schwierig sein, die Umkehrung der Kontrolle zu verstehen. Was bedeutet Kontrolle? Was wurde rückgängig gemacht?
Es wird spekuliert, dass dies daran liegt, dass Entwickler solche Frameworks von Anfang an verwendet haben und die letzte „Express- und Koa-Ära“ nicht erlebt haben und nicht über die Prügel der alten Gesellschaft verfügen. Gepaart mit der umgekehrten Formulierung wirkt das Programm sehr abstrakt und schwer verständlich.
Wie bereits erwähnt, werden bei der Implementierung von Koa-Anwendungen alle Klassen vollständig von uns gesteuert, sodass sie als herkömmliche Programmsteuerungsmethode betrachtet werden können. Daher nennen wir sie: Vorwärtsrotation steuern. Wir verwenden Nest, das unten eine Reihe von Controllern implementiert. Während des eigentlichen Entwicklungsprozesses müssen wir nur Konfigurationscode gemäß der Vereinbarung schreiben, und das Rahmenprogramm hilft uns bei der Verwaltung der Abhängigkeitsinjektion von Klassen. Deshalb nennen wir es: Umkehrung der Kontrolle.
Der Kern besteht darin, den Implementierungsprozess des Programms zur einheitlichen Verwaltung an das Rahmenprogramm zu übergeben und die Kontrollbefugnis vom Entwickler auf das Rahmenprogramm zu übertragen.
Vorwärtsdrehung steuern: rein manuelles Steuerungsprogramm des Entwicklers
Umkehrung der Kontrolle: Rahmenprogrammsteuerung
Um ein reales Beispiel zu nennen: Eine Person fährt alleine zur Arbeit und ihr Ziel ist es, das Unternehmen zu erreichen. Es fährt selbst und kontrolliert seine eigene Route. Und wenn er die Kontrolle über das Fahren abgibt und den Bus erwischt, muss er nur noch einen entsprechenden Shuttlebus auswählen, um zum Unternehmen zu gelangen. Allein im Hinblick auf die Kontrolle sind die Menschen befreit. Sie müssen sich nur noch merken, welchen Bus sie nehmen sollen, und die Wahrscheinlichkeit, Fehler zu machen, wird verringert. Das Bussystem ist der Controller und die Busleitungen sind die vereinbarten Konfigurationen.
Durch den obigen tatsächlichen Vergleich denke ich, dass ich in der Lage sein sollte, die Umkehrung der Kontrolle zu verstehen.
2.4 Zusammenfassung
Von Koa bis Nest, von Front-End-JQuery bis Vue React. Tatsächlich werden sie alle Schritt für Schritt durch Framework-Kapselung implementiert, um die Ineffizienzprobleme der vorherigen Ära zu lösen.
Die obige Koa-Anwendungsentwicklung verwendet eine sehr primitive Methode zur Steuerung von Abhängigkeiten und Instanziierung, die dem JQuery-Betriebsdom im Front-End ähnelt. Diese sehr primitive Methode wird als Kontrollweiterleitung bezeichnet, und Vue React ähnelt der von Nest bereitgestellten Programmebene Controller, sie können alle als Umkehrung der Kontrolle bezeichnet werden. Das ist auch mein persönliches Verständnis, wenn es irgendwelche Probleme gibt, hoffe ich, dass Gott sie aufzeigt.
Lassen Sie uns über das Modul @Module in Nest sprechen. Abhängigkeitsinjektion und Kontrollinversion erfordern es als Medium.
Nestjs implementiert die Umkehrung der Kontrolle und stimmt zu, die Importe, Exporte und Anbieter des Moduls (@module) zu konfigurieren, um den Anbieter zu verwalten, der die Abhängigkeitsinjektion der Klasse darstellt.
Unter Anbietern versteht man die Registrierung und Instanziierung von Klassen im aktuellen Modul. Wenn B im Konstruktor auf A verweist, verweist es auf die A-Instanz des aktuellen Moduls.
import { Module } from '@nestjs/common'; import { ModuleX } from './moduleX'; import { A } from './A'; import { B } aus './B'; @Modul({ Importe: [ModuleX], Anbieter: [A,B], Exporte: [A] }) Exportklasse ModuleD {} // B Klasse B{ Konstruktor(a:A){ this.a = a; } }
exports
beziehen sich auf Klassen, die in providers
im aktuellen Modul als Klassen instanziiert werden, die von externen Modulen gemeinsam genutzt werden können. Wenn beispielsweise die C-Klasse von ModuleF instanziiert wird, möchte ich die A-Klasseninstanz von ModuleD direkt einfügen. Legen Sie einfach Exporte A in ModuleD fest und importieren Sie ModuleD über imports
in ModuleF.
Gemäß der folgenden Schreibmethode scannt das Inversion-of-Control-Programm automatisch Abhängigkeiten. Überprüfen Sie zunächst, ob in den Anbietern Ihres eigenen Moduls eine Instanz von A vorhanden ist gefunden, wird die A-Instanz von ModuleD in die C-Instanz eingefügt.
import { Module } from '@nestjs/common'; import { ModuleD} aus './moduleD'; importiere { C } aus './C'; @Modul({ Importe: [ModuleD], Anbieter: [C], }) Exportklasse ModuleF {} // C Klasse C { Konstruktor(a:A){ this.a = a; } }
Wenn Sie möchten, dass ein externes Modul die Klasseninstanz des aktuellen Moduls verwendet, müssen Sie daher zunächst die Instanziierungsklasse in providers
des aktuellen Moduls definieren und dann diese Klasse definieren und exportieren. Andernfalls wird ein Fehler gemeldet.
//Richtiges @Module({ Anbieter: [A], Exporte: [A] }) //Fehler @Module({ Anbieter: [], Exporte: [A] })
Wenn man auf den Prozess der Instanzenfindung desspäteren
Zusatzmoduls zurückblickt, ist dies tatsächlich etwas unklar. Der Kernpunkt besteht darin, dass die Klassen in Anbietern instanziiert werden und nach der Instanziierung nur die Klassen in Anbietern im Modul instanziiert werden und Export und Import lediglich organisatorische Beziehungskonfigurationen sind. Das Modul gibt der Verwendung seines eigenen Anbieters Vorrang. Wenn nicht, prüfen Sie, ob das importierte Modul über einen entsprechenden Anbieter
verfügt
.
Konstruktor(private a: A) { } }
Da TypeScript die implizite und automatische Definition von Konstruktorparametern (privat, geschützt, öffentlich, schreibgeschützt) als Klassenattribute (Parametereigenschaft) unterstützt, besteht keine Notwendigkeit, this.a = a
zu verwenden. So steht es in Nest geschrieben.
Das Konzept der Metaprogrammierung spiegelt sich im Nest-Framework wider. Inversion of Control und Decorators sind die Implementierung der Metaprogrammierung. Es kann grob verstanden werden, dass die Essenz der Metaprogrammierung immer noch die Programmierung ist, aber es gibt einige abstrakte Programme in der Mitte. Dieses abstrakte Programm kann Metadaten identifizieren (z. B. Objektdaten in @Module), was tatsächlich eine Erweiterungsfunktion ist, die andere verwenden kann Programme als Daten zu handhaben. Wenn wir solche abstrakten Programme schreiben, handelt es sich um Metaprogrammierung.
4.1 Metadaten
Metadaten werden oft in Nest-Dokumenten erwähnt, wenn Sie sie zum ersten Mal sehen. Sie müssen sich mit der Zeit daran gewöhnen und sie verstehen, also müssen Sie sich nicht zu sehr damit vertraut machen verstrickt.
Die Definition von Metadaten lautet: Daten, die Daten beschreiben, hauptsächlich Informationen, die Datenattribute beschreiben, und können auch als Daten verstanden werden, die Programme beschreiben.
exports、providers、imports、controllers
sind allesamt Metadaten , da es sich um Daten handelt, die zur Beschreibung von Programmbeziehungen verwendet werden. Diese Dateninformationen sind nicht die tatsächlichen Daten, die dem Endbenutzer angezeigt werden, sondern werden vom gelesen und erkannt Rahmenprogramm.
4.2 Nest Decorator
Wenn Sie sich den Quellcode des Decorators in Nest ansehen, werden Sie feststellen, dass fast jeder Decorator selbst Metadaten nur über Reflect-Metadata definiert.
@Injectable Decorator
-Exportfunktion Injectable(options?: InjectableOptions): ClassDecorator { return (Ziel: Objekt) => { Reflect.defineMetadata(INJECTABLE_WATERMARK, true, target); Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, Optionen, Ziel); }; }
Hier gibt es das Konzept der Reflektion, und Reflektion ist relativ einfach zu verstehen. Nehmen Sie den @Module-Dekorator als Beispiel, um providers
zu definieren. Wenn das Programm tatsächlich ausgeführt wird, werden providers
auch der providers
sein Die Instanziierung wird automatisch vom Framework-Programm verwendet und Entwickler müssen keine Instanziierung und Abhängigkeitsinjektion durchführen. Eine Klasse wird erst dann zum Anbieter, wenn sie in einem Modul instanziiert wird. Die Klassen in providers
werden reflektiert und werden zu Anbietern. Inversion of Control ist die verwendete Reflexionstechnologie.
Ein weiteres Beispiel ist ORM (Object Relational Mapping) in der Datenbank. Um ORM zu verwenden, müssen Sie nur Tabellenfelder definieren, und die ORM-Bibliothek konvertiert die Objektdaten automatisch in SQL-Anweisungen.
const data = TableModel.build(); data.time = 1; data.browser = 'chrome'; data.save(); // SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}]
Die ORM-Bibliothek verwendet Reflexionstechnologie, sodass Benutzer nur auf die Felddaten selbst achten müssen. und das Objekt wird zu einer SQL-Ausführungsanweisung in der ORM-Bibliothek. Entwickler müssen sich nur auf Datenfelder konzentrieren und müssen kein SQL schreiben.
4.3 Reflect-Metadata
Reflect-Metadata ist eine Reflection-Bibliothek, die Nest zum Verwalten von Metadaten verwendet. Reflect-Metadata verwendet WeakMap, um eine globale Einzelinstanz zu erstellen, und legt die Metadaten des dekorierten Objekts (Klasse, Methode usw.) über die Methoden set und get fest und ruft sie ab.
// Schauen Sie sich einfach um var _WeakMap = !usePolyfill && typeof WeakMap === "function" : CreateWeakMapPolyfill(); var Metadata = new _WeakMap(); Funktion defineMetadata(){ OrdinaryDefineOwnMetadata(){ GetOrCreateMetadataMap(){ var targetMetadata = Metadata.get(O); if (IsUndefined(targetMetadata)) { if (!Create) undefiniert zurückgeben; targetMetadata = new _Map(); Metadata.set(O, targetMetadata); } var metadataMap = targetMetadata.get(P); if (IsUndefined(metadataMap)) { if (!Create) undefiniert zurückgeben; metadataMap = new _Map(); targetMetadata.set(P, metadataMap); } return metadataMap; } } }
Reflect-Metadata speichert die Metadaten der dekorierten Person im globalen Singleton-Objekt für eine einheitliche Verwaltung. Reflect-Metadata implementiert keine spezifische Reflektion, stellt jedoch eine Toolbibliothek zur Unterstützung der Reflektionsimplementierung bereit.
Schauen wir uns abschließend die vorherigen Fragen an.
Warum ist eine Umkehrung der Kontrolle erforderlich?
Was ist Abhängigkeitsinjektion?
Was macht der Dekorateur?
Was sind die Implementierungsprinzipien von Anbietern, Importen und Exporten in Modulen (@Module)?
1 und 2 Ich glaube, ich habe es schon vorher klargestellt. Wenn es immer noch etwas vage ist, empfehle ich Ihnen, es noch einmal zu lesen und einige andere Artikel zu konsultieren, um das Wissen anhand der Gedanken verschiedener Autoren zu verstehen.
5.1 Problem [3 4] Überblick:
Nest verwendet Reflexionstechnologie, um die Umkehrung der Steuerung zu implementieren und Metaprogrammierungsfunktionen bereitzustellen. Entwickler verwenden den @Module-Dekorator, um Klassen zu dekorieren und Metadaten (AnbieterImporteExporte) zu definieren. Die Metadaten werden in einem globalen Verzeichnis gespeichert Objekt (unter Verwendung der Reflect-Metadata-Bibliothek). Nachdem das Programm ausgeführt wurde, liest und registriert das Steuerungsprogramm innerhalb des Nest-Frameworks den Modulbaum, scannt die Metadaten, instanziiert die Klasse als Anbieter und stellt sie in allen Modulen gemäß der Definition „ProvidersImportsExports“ in den Modulmetadaten bereit Suchen Sie nach Instanzen (Anbietern) anderer abhängiger Klassen der aktuellen Klasse und fügen Sie sie über den Konstruktor ein, nachdem Sie sie gefunden haben.
Dieser Artikel enthält viele Konzepte und bietet keine allzu detaillierte Analyse. Es braucht Zeit, bis man die Konzepte langsam versteht. Seien Sie nicht zu besorgt. Okay, das war's. Freunde, denen er gefällt, hoffen, dass Sie ihn dreimal mit einem Klick verbinden können