Nodo: mecanismo de módulo en profundidad

Nodo: mecanismo de módulo en profundidad

1. Especificación CommonJS

  • La aparición de la especificación CommonJS ( http://www.commonjs.org ) tiene como objetivo construir un ecosistema de JavaScript que incluya servidores web, escritorios, herramientas de línea de comandos y navegadores. CommonJS en realidad no es un lenguaje nuevo, y ni siquiera podemos decir que sea un intérprete nuevo; de hecho, es solo un concepto o una especificación .

  • ¿Qué brechas en JavaScript de front-end cubre específicamente? De hecho, esto también implica una gran cantidad de cosas que no están cubiertos por el front-end de JavaScript, como binario, codificación, IO, archivos, sistemas, pruebas de la afirmación, tomas de corriente, las colas de eventos, de los trabajadores, consolas, y así sucesivamente .

  • CommonJS ha desarrollado algunas especificaciones para resolver estos problemas, y Node.js es una implementación de estas especificaciones. El propio Node.js implementa el método require como método de introducción de módulos. Al mismo tiempo, NPM también implementa funciones como la gestión de dependencias y la instalación automática de módulos basados ​​en la especificación del paquete definida por CommonJS. Aquí nos sumergiremos en el mecanismo de requerimiento de Node.js y la aplicación de NPM según la especificación del paquete.

2. Definición y uso de módulos simples

Veamos primero un módulo definido simple para calcular el área de un círculo:

// circle.js

var PI = Math.PI;

exports.area = function(r){
    
    return PI * r * r;

}

// app.js

var circle = require('./circle.js');

console.log(circle.area(2));	// 4PI

Se puede ver que la llamada del módulo es muy conveniente, solo se requieren los archivos que necesitan ser llamados; Node.js dice que la definición y la llamada del módulo están empaquetadas y son simples, lo cual es muy amigable para el usuario;

3. La diferencia entre exportaciones y module.exports

Echemos un vistazo a una pequeña castaña:

// rocker.js
module.exports = 'ROCK N ROLL!';
exports.name = function() {
    
    
    console.log('Let them know that we still rock n roll');
};

//app.js
var rocker = require('./rocker.js');
rocker.name(); // TypeError: Object ROCK N ROLL! has no method 'name'
  • El módulo de balancín ignoró por completo export.name, y luego devolvió una cadena de '¡RÓCELO!' .

  • A través del ejemplo anterior, puede darse cuenta de que sus módulos no tienen que ser instancias de módulo. Su módulo puede ser cualquier objeto JavaScript válido: booleano, número, fecha, JSON, cadena, función, matriz y otros .

  • Si no establece explícitamente ningún valor en module.exports, los atributos de las exportaciones se asignarán a module.exports y luego se devolverán .

El uso correcto es:

// rocker.js
module.exports = function(name, age) {
    
    
    this.name = name;
    this.age = age;
    this.about = function() {
    
    
        console.log(this.name +' is '+ this.age +' years old');
    };
};

//app.js
var Rocker = require('./rocker.js');
var rocker = new Rocker('yivi',18);
rocker.about();	//yivi is 18 years old
  • Si desea que su módulo sea un tipo de objeto especial, utilice module.exports; si desea que su módulo sea una instancia de módulo tradicional, utilice exportaciones .

El resultado de asignar atributos a module.exports es el mismo que asignar atributos a las exportaciones. Mira el siguiente ejemplo:

module.exports.name = function() {
    
    
    console.log('My name is Lemmy Kilmister');
};

//等同于

exports.name = function() {
    
    
    console.log('My name is Lemmy Kilmister');
};
  • Module.exports es algo real, las exportaciones son solo sus métodos auxiliares . Dicho esto, las exportaciones siguen siendo el objeto recomendado, a menos que desee cambiar el tipo de objeto de su módulo de la instancia del módulo tradicional a otra cosa .

4. Estrategia de carga de módulos

Los módulos de Node.js se dividen en dos categorías:

  • Uno es el módulo nativo (núcleo) y el otro es el módulo de archivos . El módulo nativo se compila en el archivo ejecutable binario cuando se compila el código fuente de Node.js y la velocidad de carga es la más rápida .
  • Otro tipo de módulo de archivo se carga dinámicamente y la velocidad de carga es más lenta que la del módulo nativo .

Sin embargo, Node.js almacena en caché tanto el módulo nativo como el módulo de archivo , por lo que no habrá una sobrecarga repetitiva durante el segundo requerimiento. Los módulos nativos están todos definidos en el directorio lib y los módulos de archivo son inciertos .

  • El trabajo de cargar el módulo de archivo se realiza y completa principalmente mediante el módulo de módulo nativo, que se ha cargado al inicio, y el proceso llama directamente al método estático runMain.

  • De hecho, en el módulo de archivos, se divide en 3 tipos de módulos . Estos tres tipos de módulos de archivo se distinguen por sufijos, y Node.js determinará el método de carga de acuerdo con el nombre del sufijo:

**. js: ** Leer archivos js sincrónicamente a través del módulo fs y compilarlos y ejecutarlos.

**. nodo: ** Complemento escrito en C / C ++. Cargue mediante el método dlopen.

**. json: ** Lea el archivo, llame a JSON.parse para analizar y cargar.

En el proceso de compilación de archivos js, Node.js encapsulará los archivos js al principio y al final, y los empaquetará en un módulo para que no contamine el mundo entero, así:

(function (exports, require, module, __filename, __dirname) {
    
    
    var circle = require('./circle.js');
    console.log('The area of a circle of radius 4 is ' + circle.area(4));
});
  • Este código será runInThisContextejecutado por el método del módulo nativo vm (similar a eval, pero tiene un contexto claro y no contamina el global), y regresa como un objeto de función específico. Finalmente, el objeto de módulo pasado exports, el requiremétodo, módulo, nombre de archivo y nombre de directorio se utilizan como parámetros reales y se ejecutan.
  • Es por eso que require no está definido en el archivo app.js, pero este método existe. A partir de la documentación de la API Node.js se puede ver allí __filename, __dirname, module, exportsvarias variables definidas, pero no existe. Que __filenamey __dirnamedespués del análisis obtenido en el proceso para encontrar la ruta del archivo pasado. La variable del módulo es el objeto del módulo en sí, exportsun objeto vacío ({}, no nulo) inicializado en el constructor del módulo.
  • En este archivo principal, los otros módulos se pueden introducir mediante el método require. De hecho, este método require realmente llama al método de carga .
  • El método de carga devuelve el objeto de exportación del módulo después de cargar, compilar y almacenar en caché el módulo. Esta es la razón por la que solo los métodos definidos en el objeto de exportación en el archivo circle.js se pueden llamar externamente .

El mecanismo de carga del módulo descrito anteriormente se define en lib / module.js.

5. Estrategia de búsqueda de archivos en el método obligatorio

  1. Cargar desde la memoria caché del módulo de archivos
  2. Cargar desde módulo nativo
  3. Carga del archivo

El método require recibe los siguientes parámetros:

http, fs, ruta, etc., módulos nativos.

/ mod o ... / mod, el módulo de archivo de la ruta relativa.

/ pathtomodule / mod, la ruta absoluta del módulo de archivo.

mod, un módulo de archivo de un módulo no nativo.

Para cada módulo de archivo cargado, cuando se crea el objeto de módulo, el módulo tendrá un atributo de rutas cuyo valor se calcula en función de la ruta del archivo actual.

La regla de generación de ruta es: busque el directorio node_modules desde el directorio de archivos actual; luego ingrese el directorio principal a su vez, busque el directorio node_modules debajo del directorio principal; itere sucesivamente hasta el directorio node_modules debajo del directorio raíz.

Si se requiere el archivo en la ruta absoluta, la búsqueda no atravesará cada directorio node_modules de adentro hacia afuera, que es el más rápido.

6. Estructura del paquete

Como se mencionó anteriormente, JavaScript carece de una estructura de paquete. CommonJS se compromete a cambiar este status quo, por lo que define la especificación de la estructura del paquete ( http://wiki.commonjs.org/wiki/Packages/1.0 ). La aparición de NPM tiene como objetivo resolver los problemas de instalación y desinstalación de paquetes, gestión de dependencias, gestión de versiones, etc. basándose en la especificación CommonJS . Una vez que el mecanismo de búsqueda de require esté claro, veamos los detalles del paquete.

Debe existir un archivo package.json en el directorio de nivel superior del paquete.

El archivo binario debe estar contenido en el directorio bin.

El código JavaScript debe incluirse en el directorio lib.

El documento debe estar en el directorio doc.

La prueba unitaria debe estar en el directorio de prueba.

A partir del requireproceso de búsqueda anterior , podemos saber que cuando Node.js no encuentra el archivo de destino, intentará cargar el directorio actual como un paquete, por lo que el campo más importante del archivo package.json es main . Pero, de hecho, este lugar es una extensión de Node.js, este campo no está incluido en la definición estándar, ya que requiresolo se necesita el atributo principal . Pero además, el paquete debe aceptar la instalación, desinstalación, administración de dependencias, administración de versiones y otros procesos, por lo que CommonJS define los siguientes campos obligatorios para el archivo package.json:

name: el nombre del paquete, que debe ser único en NPM. No puede contener espacios.

descripción: Introducción al paquete. Suele aparecer en algunas listas.

versión: número de versión. Un número de versión semántica ( http://semver.org/ ), generalmente xyz. Este número de versión es muy importante y se usa a menudo en algunas ocasiones de control de versiones.

palabras clave: una variedad de palabras clave. Se utiliza para la búsqueda de categorías en NPM.

mantenedores: una serie de mantenedores de paquetes. El elemento de matriz es un objeto JSON que contiene tres atributos: nombre, correo electrónico y web.

contribuidores: una serie de contribuyentes de paquetes. El primero es el autor del paquete. En la comunidad de código abierto, si el parche enviado se fusiona con la rama maestra, se debe agregar a la persona que contribuyó con el parche. El formato incluye nombre y correo electrónico.

errores: una dirección URL a la que puede enviar errores. Puede ser una dirección de correo (mailto: mailxx @ dominio) o una dirección de página web ( http: // url ).

licencias: las licencias utilizadas por el paquete.

repositorios: conjunto de direcciones donde se aloja el código fuente.

dependencias: las dependencias requeridas por el paquete actual. Este atributo es muy importante, NPM usará este atributo para ayudarlo a cargar automáticamente los paquetes dependientes.

Secuencias de comandos: especifique qué archivo se ejecutará durante la operación o ejecute un comando .

7. Las similitudes y diferencias entre el módulo Node.js y el módulo front-end

Por lo general, hay algunos módulos que se pueden aplicar al principio y al final, pero los archivos JavaScript cargados por el navegador a través de la etiqueta de secuencia de comandos están desnudos, y Node.js se empaqueta durante la carga hasta el proceso de ejecución final, de modo que las variables de cada archivo están encapsulados en un cierre, no contaminarán las variables globales.

Entonces, para resolver el problema de la coherencia de front-end y back-end, los desarrolladores de bibliotecas de clases deben envolver el código de la biblioteca de clases en un cierre .

Por lo tanto, al diseñar una biblioteca de JavaScript universal para front y back end, existen juicios similares a los siguientes:

if (typeof exports !== "undefined") {
    
    
    exports.EventProxy = EventProxy;
} else {
    
    
    this.EventProxy = EventProxy;
}

Es decir, si el objeto de exportación existe, la variable local se monta en el objeto de exportación, si no existe, se monta en el objeto global .

Supongo que te gusta

Origin blog.csdn.net/yivisir/article/details/107772449
Recomendado
Clasificación