
Cómo comenzar rápidamente con VUE3.0: ingrese y aprenda
Nest proporciona un mecanismo de módulo para completar la inyección de dependencia definiendo proveedores, importaciones, exportaciones y constructores de proveedores en el decorador de módulos, y el desarrollo de toda la aplicación se organiza a través de él. árbol de módulos. No hay absolutamente ningún problema en iniciar directamente una aplicación de acuerdo con las convenciones del propio marco. Sin embargo, siento que me falta una comprensión más clara y sistemática de la inyección de dependencia, la inversión de control, los módulos, los proveedores, los metadatos, los decoradores relacionados, etc. declarados por el marco.
- ¿Por qué es necesaria la inversión del control?
- ¿Qué es la inyección de dependencia?
- ¿Qué hace el decorador?
- ¿Cuáles son los principios de implementación de proveedores, importaciones y exportaciones en módulos (@Module)?
Parece que puedo entenderlo y apreciarlo, pero déjame explicarlo claramente desde el principio, no puedo explicarlo claramente. Entonces investigué un poco y se me ocurrió este artículo. A partir de ahora empezamos desde cero e ingresamos el texto principal.
1.1 Express, Koa
El proceso de desarrollo de un lenguaje y su comunidad técnica debe enriquecerse y desarrollarse gradualmente desde las funciones inferiores hacia arriba, al igual que el proceso de las raíces de los árboles que crecen lentamente hasta convertirse en ramas y luego se llenan de hojas. Anteriormente, aparecieron en Nodejs marcos de servicios web básicos como Express y Koa. Capaz de proporcionar una capacidad de servicio muy básica. Sobre la base de dicho marco, una gran cantidad de middleware y complementos comenzaron a nacer en la comunidad, proporcionando servicios más completos para el marco. Necesitamos organizar las dependencias de las aplicaciones y crear nosotros mismos el andamiaje de las aplicaciones, lo cual es flexible y engorroso, y también requiere una cierta carga de trabajo.
Más adelante en el desarrollo, nacieron algunos marcos con una producción más eficiente y reglas más unificadas, lo que marcó el comienzo de una nueva etapa.
1.2 EggJs y Nestjs
Para ser más adaptables a las aplicaciones de producción rápida, unificar estándares y ponerlos a disposición de manera inmediata, se han desarrollado marcos como EggJs, NestJs y Midway. Este tipo de marco abstrae la implementación de una aplicación en un proceso universal y extensible mediante la implementación del ciclo de vida subyacente. Solo necesitamos seguir el método de configuración proporcionado por el marco para implementar la aplicación de manera más simple. El marco implementa el control de procesos del programa, y solo necesitamos ensamblar nuestras piezas en la ubicación adecuada. Esto se parece más al trabajo de una línea de ensamblaje. Cada proceso está claramente dividido y se ahorran muchos costos de implementación.
1.3 Resumen
Las dos etapas anteriores son solo un presagio. Podemos entender a grandes rasgos que la actualización del marco mejora la eficiencia de la producción. Para lograr la actualización del marco, se introducirán algunas ideas y patrones de diseño en Nest. inyección de dependencia y los conceptos de metaprogramación, hablemos de ello a continuación.
2.1 Inyección de dependencia
Una aplicación es en realidad una gran cantidad de clases abstractas que realizan todas las funciones de la aplicación llamándose entre sí. A medida que aumenta la complejidad del código y las funciones de la aplicación, el proyecto definitivamente será cada vez más difícil de mantener porque hay cada vez más clases y las relaciones entre ellas se vuelven cada vez más complejas.
Por ejemplo, si usamos Koa para desarrollar nuestra aplicación, Koa implementa principalmente un conjunto de capacidades básicas de servicios web. En el proceso de implementación de la aplicación, definiremos muchas clases, los métodos de creación de instancias y las interdependencias de estas clases. ser organizado y controlado libremente por nosotros en la lógica del código. La creación de instancias de cada clase es nueva manualmente para nosotros, y podemos controlar si una clase solo se crea una instancia una vez y luego se comparte, o si se crea una instancia cada vez. La siguiente clase B depende de A. Cada vez que se crea una instancia de B, se creará una instancia de A una vez, por lo que para cada instancia de B, A es una instancia que no se comparte.
clase A{}
// B
clase B{
constructor(){
this.a = nueva A();
}
} La siguiente C es la instancia externa obtenida, por lo que varias instancias de C comparten la instancia app.a.
clase A{}
// C
aplicación constante = {};
aplicación.a = nueva A();
clase C{
constructor(){
this.a = aplicación.a;
}
} La siguiente D se pasa a través del parámetro del constructor. Puede pasar una instancia no compartida cada vez, o puede pasar la instancia compartida de app.a (D y F comparten app.a), y por la forma. ahora es un parámetro Pasar, también puedo pasar una instancia de clase X.
clase A{}
clase X{}
//D
aplicación constante = {};
aplicación.a = nueva A();
clase D{
constructor(a){
esto.a = a;
}
}
clase F {
constructor(a){
esto.a = a;
}
}
nueva D (aplicación a)
nueva F (aplicación a)
nueva
D(nueva
La inyección a través del constructor (pasando por valor) es solo un método de implementación. También se puede pasar implementando la llamada al método set, o cualquier otro método, siempre que se pueda pasar una dependencia externa a la interna. Es realmente así de simple.
clase A{}
//D
clase D{
establecerDep(a){
esto.a = a;
}
}
constante d = nueva D()
d.setDep(new A()) 2.2 ¿Todo en inyección de dependencia?
A medida que avanza la iteración, parece que las dependencias de B cambiarán según diferentes condiciones previas. Por ejemplo, la condición previa uno this.a debe pasarse en el caso de A, y la condición previa dos this.a debe pasarse en el caso de X. En este momento, comenzaremos a hacer la abstracción real. Lo transformaremos en un método de inyección de dependencia como el D anterior.
Al principio, cuando implementamos la aplicación, implementábamos el método de escritura de las clases B y C siempre que cumpliera con las necesidades en ese momento. Esto en sí no fue un problema después de que el proyecto se iterara durante varios años. del código no necesariamente se tocaría. Si consideramos una expansión posterior, afectará la eficiencia del desarrollo y puede que no sea útil. Entonces, la mayoría de las veces, nos encontramos con escenarios que requieren abstracción y luego transformamos de manera abstracta parte del código.
// clase B{ antes de la transformación
constructor(){
this.a = nueva A();
}
}
nueva B()
//Clase D{ después de la transformación
constructor(a){
esto.a = a;
}
}
nueva D(nueva A())
nueva D (
nuevoscostos de implementación).
Este ejemplo se da aquí para ilustrar eso en un modelo de desarrollo sin restricciones ni regulaciones. Podemos escribir código libremente para controlar las dependencias entre varias clases. En un entorno completamente abierto, es muy libre. Esta es una era primitiva de agricultura de tala y quema. Dado que no existe un modelo de desarrollo de código fijo ni un plan de acción máximo, a medida que intervienen diferentes desarrolladores o el mismo desarrollador escribe código en diferentes momentos, la relación de dependencia será muy diferente a medida que el código crezca. Claramente, la instancia compartida puede tener instancias múltiples. , desperdiciando memoria. A partir del código, es difícil ver una estructura de dependencia completa y el código puede resultar muy difícil de mantener.

Luego, cada vez que definimos una clase, la escribimos de acuerdo con el método de inyección de dependencia y la escribimos como D. Luego avanza el proceso de abstracción de C y B, lo que hace que la expansión posterior sea más conveniente y reduce el costo de la transformación. Entonces esto se llama All in 依赖注入, es decir, todas nuestras dependencias se implementan mediante inyección de dependencia.
Sin embargo, el costo de la implementación inicial vuelve a ser alto y es difícil lograr unidad y perseverancia en la colaboración del equipo. Al final, la implementación puede fallar. Esto también se puede definir como un diseño excesivo, porque el costo de implementación adicional puede no ser suficiente. necesariamente lograrse.
2.3 Inversión de control
Ahora que hemos acordado el uso unificado de la inyección de dependencia, ¿podemos implementar un controlador de nivel inferior a través de la encapsulación subyacente del marco y acordar una regla de configuración de dependencia? La configuración de dependencia que definimos y el intercambio de dependencias nos ayudan a lograr la gestión de clases. Este patrón de diseño se llama inversión de control .
La inversión del control puede resultar difícil de entender cuando la escuche por primera vez. ¿Qué significa control? ¿Qué se revirtió?
Se especula que esto se debe a que los desarrolladores han utilizado dichos marcos desde el principio y no han experimentado la última "era Express y Koa" y carecen de los golpes de la vieja sociedad. Junto con la redacción invertida, el programa parece muy abstracto y difícil de entender.
Como mencionamos anteriormente, al implementar aplicaciones Koa, todas las clases están completamente controladas por nosotros, por lo que puede considerarse como un método de control de programa convencional, por eso lo llamamos: control de rotación hacia adelante. Usamos Nest, que implementa un conjunto de controladores en la parte inferior. Solo necesitamos escribir el código de configuración de acuerdo con el acuerdo durante el proceso de desarrollo real, y el programa marco nos ayudará a administrar la inyección de dependencia de clases, por eso lo llamamos: inversión de control.
La esencia es entregar el proceso de implementación del programa al programa marco para una gestión unificada y transferir el poder de control del desarrollador al programa marco.
Controlar la rotación hacia adelante: programa de control puramente manual del desarrollador
Inversión de control: control del programa marco
Por poner un ejemplo real, una persona conduce sola al trabajo y su objetivo es llegar a la empresa. Se conduce solo y controla su propia ruta. Y si cede el control de la conducción y toma el autobús, sólo tendrá que elegir el autobús lanzadera correspondiente para llegar a la empresa. Sólo en términos de control, las personas se sienten liberadas. Sólo necesitan recordar qué autobús tomar. La posibilidad de cometer errores también se reduce y la gente está mucho más relajada. El sistema de bus es el controlador y las líneas de bus son las configuraciones acordadas.
A través de la comparación real anterior, creo que debería poder comprender la inversión del control.
2.4 Resumen
De Koa a Nest, de front-end JQuery a Vue React. De hecho, todos se implementan paso a paso mediante la encapsulación del marco para resolver los problemas de ineficiencia de la era anterior.
El desarrollo de la aplicación Koa anterior utiliza una forma muy primitiva de controlar las dependencias y la creación de instancias, que es similar al sistema operativo JQuery en el front-end. Esta forma muy primitiva se llama reenvío de control, y Vue React es como lo que proporciona Nest Una capa de programa. controlador, todos ellos pueden denominarse inversión de control. Este también es mi entendimiento personal. Si hay algún problema, espero que Dios lo señale.
Hablemos del módulo @Module en Nest. La inyección de dependencia y la inversión de control lo requieren como medio.
Nestjs implementa la inversión de control y acepta configurar las importaciones, exportaciones y proveedores del módulo (@module) para administrar el proveedor, que es la inyección de dependencia de la clase.
Se puede entender que los proveedores registran y crean instancias de clases en el módulo actual. Los siguientes A y B se crean instancias en el módulo actual. Si B hace referencia a A en el constructor, se refiere a la instancia A del MóduloD actual.
importar {Módulo} desde '@nestjs/common';
importar {MóduloX} desde './móduloX';
importar {A} desde './A';
importar {B} desde './B';
@Módulo({
importa: [MóduloX],
proveedores: [A,B],
exportaciones: [A]
})
clase de exportación MóduloD {}
// B
clase B{
constructor(a:A){
esto.a = a;
}
} exports se refiere a clases instanciadas en providers en el módulo actual como clases que pueden ser compartidas por módulos externos. Por ejemplo, cuando se crea una instancia de la clase C de ModuleF, quiero inyectar directamente la instancia de clase A de ModuleD. Simplemente configure las exportaciones A en ModuleD e importe ModuleD a través de imports en ModuleF.
De acuerdo con el siguiente método de escritura, el programa de control de inversión escaneará automáticamente las dependencias. Primero, verifique si existe el proveedor A en los proveedores de su propio módulo. Si no, busque una instancia de A en el MóduloD importado. encontrado, obtenga la instancia A del MóduloD y se inyecte en la instancia C.
importar {Módulo} desde '@nestjs/common';
importar {MóduloD} desde './móduloD';
importar {C} desde './C';
@Módulo({
importaciones: [MóduloD],
proveedores: [C],
})
clase de exportación MóduloF {}
// C
clase C {
constructor(a:A){
esto.a = a;
}
} Por lo tanto, si desea que un módulo externo use la instancia de clase del módulo actual, primero debe definir la clase de creación de instancias en providers del módulo actual y luego definir y exportar esta clase; de lo contrario, se informará un error.
//Correcto @Module({
proveedores: [A],
exportaciones: [A]
})
//Error @Módulo({
proveedores: [],
exportaciones: [A]
}) Mirando hacia atrás en el proceso de búsqueda de instancias del módulocomplementario posterior
, de hecho no está claro. El punto central es que se crearán instancias de las clases de los proveedores y, después de la creación de instancias, se convertirán en proveedores. Solo se crearán instancias de las clases de los proveedores en el módulo, y la exportación y la importación son solo configuraciones de relaciones organizativas. El módulo dará prioridad al uso de su propio proveedor. De lo contrario, verifique si el módulo importado tiene un proveedor correspondiente.
Permítanme
mencionar algunos puntos de conocimiento de ts.
constructor(privado a: A) {
}
} Dado que TypeScript admite que los parámetros del constructor (privados, protegidos, públicos, de solo lectura) se definan implícita y automáticamente como atributos de clase (Propiedad de parámetro), no es necesario usar this.a = a . Así está escrito en Nest.
El concepto de metaprogramación se refleja en el marco Nest. La inversión de control y los decoradores son la implementación de la metaprogramación. Se puede entender a grandes rasgos que la esencia de la metaprogramación sigue siendo la programación, pero hay algunos programas abstractos en el medio. Este programa abstracto puede identificar metadatos (como los datos de objetos en @Module), que en realidad es una capacidad de expansión que puede usar otros. programas como datos para manejar. Cuando escribimos programas tan abstractos, estamos metaprogramando.
4.1 Metadatos
Los metadatos se mencionan a menudo en los documentos de Nest. El concepto de metadatos puede resultar confuso cuando lo ve por primera vez. Es necesario acostumbrarse a él y comprenderlo a medida que pasa el tiempo, por lo que no es necesario hacerlo demasiado. enredado.
La definición de metadatos es: datos que describen datos, principalmente información que describe atributos de datos, y también pueden entenderse como datos que describen programas.
exports、providers、imports、controllers configurados por @Module en Nest son todos metadatos , porque son datos utilizados para describir las relaciones del programa. Esta información de datos no son los datos reales que se muestran al usuario final, pero el usuario los lee y los reconoce. programa marco.
4.2 Decorador de Nest
Si observa el código fuente del decorador en Nest, encontrará que casi todos los decoradores solo definen metadatos a través de metadatos reflectantes.
@Injectable
función de exportación del decorador Injectable (¿opciones?: InjectableOptions): ClassDecorator {
retorno (destino: objeto) => {
Reflect.defineMetadata(INJECTABLE_WATERMARK, verdadero, objetivo);
Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, opciones, destino);
};
} Aquí existe el concepto de reflexión, y la reflexión es relativamente fácil de entender. Tome el decorador @Module como ejemplo para definir providers de metadatos. Simplemente pasa clases a la matriz providers . Cuando el programa realmente se ejecuta, las clases en providers . El programa marco lo utiliza automáticamente. La creación de instancias se convierte en un proveedor y los desarrolladores no necesitan realizar explícitamente la creación de instancias y la inyección de dependencia. Una clase solo se convierte en proveedor después de que se crea una instancia de ella en un módulo. Las clases en providers se reflejan y se convierten en proveedores. La inversión de control es la tecnología de reflexión utilizada.
Otro ejemplo es ORM (Mapeo relacional de objetos) en la base de datos. Para usar ORM, solo necesita definir campos de tabla y la biblioteca ORM convertirá automáticamente los datos del objeto en declaraciones SQL.
datos constantes = TableModel.build();
datos.tiempo = 1;
datos.browser = 'cromo';
datos.guardar();
// SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}] La biblioteca ORM utiliza tecnología de reflexión, por lo que los usuarios solo deben prestar atención a los datos del campo en sí. y el objeto es la reflexión de la biblioteca ORM y se convierte en una declaración de ejecución de SQL. Los desarrolladores solo necesitan centrarse en los campos de datos y no necesitan escribir SQL.
4.3 reflect-metadata
reflect-metadata es una biblioteca de reflexión que Nest utiliza para gestionar metadatos. reflect-metadata usa WeakMap para crear una instancia única global y establece y obtiene los metadatos del objeto decorado (clase, método, etc.) a través de los métodos set y get.
// Solo echa un vistazo var _WeakMap = !usePolyfill && typeof WeakMap === "función" WeakMap : CreateWeakMapPolyfill();
var Metadatos = nuevo _WeakMap();
función definirMetadatos(){
OrdinarioDefineOwnMetadata(){
GetOrCreateMetadataMap(){
var targetMetadata = Metadata.get(O);
if (IsUndefinido(objetivoMetadata)) {
si (!Crear)
devolver indefinido;
targetMetadata = nuevo _Map();
Metadata.set(O, targetMetadata);
}
var metadataMap = targetMetadata.get(P);
if (IsUndefinido (mapa de metadatos)) {
si (!Crear)
devolver indefinido;
metadatosMap = nuevo _Map();
targetMetadata.set(P, metadataMap);
}
devolver mapa de metadatos;
}
}
} reflect-metadata almacena los metadatos de la persona condecorada en el objeto singleton global para una gestión unificada. reflect-metadata no implementa una reflexión específica, pero proporciona una biblioteca de herramientas para ayudar en la implementación de la reflexión.
, veamos las preguntas anteriores.
¿Por qué es necesaria la inversión del control?
¿Qué es la inyección de dependencia?
¿Qué hace el decorador?
¿Cuáles son los principios de implementación de proveedores, importaciones y exportaciones en módulos (@Module)?
1 y 2 Creo que lo he dejado claro antes, si todavía es un poco vago, le sugiero que vuelva a leerlo y consulte algunos otros artículos para ayudarlo a comprender el conocimiento a través del pensamiento de diferentes autores.
5.1 Problema [3 4] Descripción general:
Nest usa tecnología de reflexión para implementar la inversión de control y proporciona capacidades de metaprogramación. Los desarrolladores usan el decorador @Module para decorar clases y definir metadatos (proveedoresimportacionesexportaciones), y los metadatos se almacenan de forma global. objeto (usando la biblioteca de metadatos reflectantes). Después de que se ejecuta el programa, el programa de control dentro del marco de Nest lee y registra el árbol de módulos, escanea los metadatos y crea una instancia de la clase para convertirla en un proveedor, y la proporciona en todos los módulos de acuerdo con la definición de proveedoresimportacionesexportaciones en los metadatos del módulo. Busque instancias (proveedores) de otras clases dependientes de la clase actual e inyéctelas a través del constructor después de encontrarlas.
Este artículo tiene muchos conceptos y no proporciona un análisis demasiado detallado. Los conceptos tardan en comprenderse lentamente. Si no los comprende a fondo en este momento, no se preocupe demasiado. Bien, eso es todo. Este artículo aún requirió mucho esfuerzo. Los amigos a quienes les guste, espero que puedan conectarse tres veces con un solo clic.