¿Cuál es el propósito de Node.js module.exports y cómo se usa?

1432

¿Cuál es el propósito de Node.js module.exports y cómo se usa?

Parece que no puedo encontrar ninguna información sobre esto, pero parece ser una parte bastante importante de Node.js, ya que a menudo lo veo en el código fuente.

De acuerdo con la documentación de Node.js :

módulo

Una referencia a la corriente module. En particular module.exports es lo mismo que el objeto de exportaciones. Ver src/node.jspara más información.

Pero esto realmente no ayuda.

¿Qué hace exactamente module.exportsy cuál sería un ejemplo simple?

mrwooster
fuente

Respuestas:

1590

module.exportses el objeto que en realidad se devuelve como resultado de una requirellamada.

La exportsvariable se establece inicialmente en ese mismo objeto (es decir, es un "alias" abreviado), por lo que en el código del módulo generalmente escribiría algo como esto:

let myFunc1 = function() { ... };
let myFunc2 = function() { ... };
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;

exportar (o "exponer") las funciones de ámbito interno myFunc1y myFunc2.

Y en el código de llamada usarías:

const m = require('./mymodule');
m.myFunc1();

donde la última línea muestra cómo el resultado de require(por lo general) es solo un objeto simple a cuyas propiedades se puede acceder.

NB: si sobrescribe exports, ya no se hará referencia module.exports. Entonces, si desea asignar un nuevo objeto (o una referencia de función), exportsentonces también debe asignar ese nuevo objeto amodule.exports


Vale la pena señalar que el nombre agregado al exportsobjeto no tiene que ser el mismo que el nombre de alcance interno del módulo para el valor que está agregando, por lo que podría tener:

let myVeryLongInternalName = function() { ... };
exports.shortName = myVeryLongInternalName;
// add other objects, functions, as required

seguido por:

const m = require('./mymodule');
m.shortName(); // invokes module.myVeryLongInternalName
Alnitak
fuente
119
Buena respuesta: me parece que 'expone' habría sido una mejor opción de terminología que 'exportaciones'
UpTheCreek
2
@ApopheniaOverload: puede hacer "exports.func1, exports.func2, etc." para tener varios métodos expuestos desde un archivo.
hellatan
73
El módulo require debe ser var m = require ('./ mymodule'); , con el punto y la barra inclinada. De esta manera, Node.js sabe que estamos usando un módulo local.
Gui Premonsa
77
Asegúrese de usar la sintaxis require ('./ module_name') porque puede haber otros módulos node.js con algún nombre y, en lugar de elegir su propio módulo, elegirá el que está instalado con node.js
Sazid el
3
@UpTheCreek existe una larga tradición de referirse a los símbolos públicos expuestos por un módulo como 'exportados', que se remontan a muchos sistemas de programación y décadas. Este no era un término nuevo inventado por los desarrolladores de Node.
Mark Reed
218

Esto ya ha sido respondido pero quería agregar algunas aclaraciones ...

Puede usar ambos exportsy module.exportspara importar código en su aplicación de esta manera:

var mycode = require('./path/to/mycode');

El caso de uso básico que verá (por ejemplo, en el código de ejemplo de ExpressJS) es que establece propiedades en el exportsobjeto en un archivo .js que luego importa utilizandorequire()

Entonces, en un ejemplo de conteo simple, podría tener:

(counter.js):

var count = 1;

exports.increment = function() {
    count++;
};

exports.getCount = function() {
    return count;
};

... luego en su aplicación (web.js, o realmente cualquier otro archivo .js):

var counting = require('./counter.js');

console.log(counting.getCount()); // 1
counting.increment();
console.log(counting.getCount()); // 2

En términos simples, puede pensar en los archivos necesarios como funciones que devuelven un solo objeto, y puede agregar propiedades (cadenas, números, matrices, funciones, cualquier cosa) al objeto que se devuelve al configurarlos exports.

A veces, querrá que el objeto devuelto de una require()llamada sea una función a la que pueda llamar, en lugar de solo un objeto con propiedades. En ese caso, también debe establecer module.exports, de esta manera:

(sayhello.js):

module.exports = exports = function() {
    console.log("Hello World!");
};

(app.js):

var sayHello = require('./sayhello.js');
sayHello(); // "Hello World!"

La diferencia entre exportaciones y module.exports se explica mejor en esta respuesta aquí .

Jed Watson
fuente
¿Cómo puedo llamar para requerir algún módulo de otra carpeta que no tenga alguna carpeta raíz como la mía?
Igal
@ user301639 puede usar rutas relativas para recorrer la jerarquía del sistema de archivos. requirecomienza en relación con la carpeta en la que se ejecuta node app.js. Le recomiendo que publique una nueva pregunta con código explícito + ejemplos de estructura de carpetas para obtener una respuesta más clara.
Jed Watson
1
Tuve que ajustar tu módulo. Ejemplo de exportaciones para que funcione. archivo: var sayHello = require('./ex6_module.js'); console.log(sayHello());y módulo:module.exports = exports = function() { return "Hello World!"; }
Jason Lydon
1
Encontré el ejemplo de incremento realmente bueno y lo he usado para refrescar mi mente cada vez que me sobrecargo con lo que estoy haciendo con las exportaciones.
munkee
module.exports = exports = function(){...}el segundo exportses solo una variable ¿verdad? En otras palabras, puede sermodule.exports = abc = function()
Jeb50
60

Tenga en cuenta que el mecanismo del módulo NodeJS se basa en módulos CommonJS que son compatibles con muchas otras implementaciones como RequireJS , pero también SproutCore , CouchDB , Wakanda , OrientDB , ArangoDB , RingoJS , TeaJS , SilkJS , curl.js o incluso Adobe Photoshop (a través de PSLib ) Puede encontrar la lista completa de implementaciones conocidas aquí .

A menos que su módulo use funciones o módulos específicos del nodo, le recomiendo que lo use en exportslugar de lo module.exports que no forma parte del estándar CommonJS y, en general, no es compatible con otras implementaciones.

Otra característica específica de NodeJS es cuando asigna una referencia a un nuevo objeto en exportslugar de simplemente agregarle propiedades y métodos, como en el último ejemplo proporcionado por Jed Watson en este hilo. Yo personalmente desaconsejaría esta práctica ya que esto rompe el soporte de referencia circular del mecanismo de módulos CommonJS. Entonces no es compatible con todas las implementaciones y el ejemplo Jed debería escribirse de esta manera (o uno similar) para proporcionar un módulo más universal:

(sayhello.js):

exports.run = function() {
    console.log("Hello World!");
}

(app.js):

var sayHello = require('./sayhello');
sayHello.run(); // "Hello World!"

O usando las funciones de ES6

(sayhello.js):

Object.assign(exports, {
    // Put all your public API here
    sayhello() {
        console.log("Hello World!");
    }
});

(app.js):

const { sayHello } = require('./sayhello');
sayHello(); // "Hello World!"

PD: Parece que Appcelerator también implementa módulos CommonJS, pero sin el soporte de referencia circular (ver: módulos Appcelerator y CommonJS (almacenamiento en caché y referencias circulares) )

Alexandre Morgaut
fuente
35

Algunas cosas que debe tener cuidado si asigna una referencia a un nuevo objeto exportsy / o modules.exports:

1. Todas las propiedades / métodos previamente adjuntos al original exportso module.exports, por supuesto, se pierden porque el objeto exportado ahora hará referencia a otro nuevo

Esto es obvio, pero si agrega un método exportado al comienzo de un módulo existente, asegúrese de que el objeto exportado nativo no haga referencia a otro objeto al final

exports.method1 = function () {}; // exposed to the original exported object
exports.method2 = function () {}; // exposed to the original exported object

module.exports.method3 = function () {}; // exposed with method1 & method2

var otherAPI = {
    // some properties and/or methods
}

exports = otherAPI; // replace the original API (works also with module.exports)

2. En caso de que uno de exportso module.exportshaga referencia a un nuevo valor, ya no hacen referencia al mismo objeto

exports = function AConstructor() {}; // override the original exported object
exports.method2 = function () {}; // exposed to the new exported object

// method added to the original exports object which not exposed any more
module.exports.method3 = function () {}; 

3. Consecuencia complicada. Si cambia la referencia a ambos exportsy module.exports, es difícil decir qué API está expuesta (parece que module.exportsgana)

// override the original exported object
module.exports = function AConstructor() {};

// try to override the original exported object
// but module.exports will be exposed instead
exports = function AnotherConstructor() {}; 
Alexandre Morgaut
fuente
29

la propiedad module.exports o el objeto de exportaciones permite que un módulo seleccione lo que se debe compartir con la aplicación

ingrese la descripción de la imagen aquí

Tengo un video sobre module_export disponible aquí

anish
fuente
18

Al dividir el código de su programa en varios archivos, module.exportsse utiliza para publicar variables y funciones para el consumidor de un módulo. La require()llamada en su archivo fuente se reemplaza con la correspondiente module.exportscargada desde el módulo.

Recuerde cuando escriba módulos

  • Las cargas del módulo se almacenan en caché, solo la llamada inicial evalúa JavaScript.
  • Es posible usar variables y funciones locales dentro de un módulo, no todo necesita ser exportado.
  • El module.exportsobjeto también está disponible como exportstaquigrafía. Pero cuando devuelva una única función, use siempre module.exports.

diagrama de exportaciones del módulo

Según: "Módulos Parte 2 - Módulos de escritura" .

pspi
fuente
9

el enlace de referencia es así:

exports = module.exports = function(){
    //....
}

las propiedades de exportso module.exports, como funciones o variables, se expondrán fuera

Hay algo a lo que debe prestar más atención: no overrideexportar.

por qué ?

debido a que exporta solo la referencia de module.exports, puede agregar las propiedades a las exportaciones, pero si anula las exportaciones, el enlace de referencia se romperá.

buen ejemplo :

exports.name = 'william';

exports.getName = function(){
   console.log(this.name);
}

mal ejemplo :

exports = 'william';

exports = function(){
     //...
}

Si solo desea exponer solo una función o variable, así:

// test.js
var name = 'william';

module.exports = function(){
    console.log(name);
}   

// index.js
var test = require('./test');
test();

este módulo solo expuso una función y la propiedad de nombre es privada para el exterior.

qianjiahao
fuente
6

Hay algunos módulos predeterminados o existentes en node.js cuando descarga e instala node.js como http, sys, etc.

Como ya están en node.js, cuando queremos usar estos módulos básicamente nos gustan los módulos de importación , pero ¿por qué? porque ya están presentes en el nodo.js. Importar es como tomarlos de node.js y ponerlos en su programa. Y luego usándolos.

Considerando que las exportaciones es exactamente lo contrario, está creando el módulo que desea, digamos el módulo add.js y colocando ese módulo en el node.js, lo hace al exportarlo.

Antes de escribir algo aquí, recuerde, module.exports.additionTwo es lo mismo que exportaciones.additionTwo

Huh, esa es la razón, nos gusta

exports.additionTwo = function(x)
{return x+2;};

Ten cuidado con el camino

Digamos que ha creado un módulo add.js,

exports.additionTwo = function(x){
return x + 2;
};

Cuando ejecuta esto en su símbolo del sistema NODE.JS:

node
var run = require('addition.js');

Esto se equivocará diciendo

Error: no se puede encontrar el módulo add.js

Esto se debe a que el proceso node.js no puede agregar.js ya que no mencionamos la ruta. Entonces, podemos establecer la ruta usando NODE_PATH

set NODE_PATH = path/to/your/additon.js

¡Ahora, esto debería ejecutarse con éxito sin ningún error!

Una cosa más, también puede ejecutar el archivo add.js al no configurar NODE_PATH, de vuelta a su símbolo del sistema nodejs:

node
var run = require('./addition.js');

Dado que estamos proporcionando la ruta aquí al decir que está en el directorio actual, ./esto también debería ejecutarse con éxito.

JumpMan
fuente
1
¿Es exportación o exportaciones?
rudrasiva86
Gracias por la ayuda :)
JumpMan
3

Un módulo encapsula el código relacionado en una sola unidad de código. Al crear un módulo, esto puede interpretarse como mover todas las funciones relacionadas a un archivo.

Supongamos que hay un archivo Hello.js que incluye dos funciones

sayHelloInEnglish = function() {
  return "Hello";
};
sayHelloInSpanish = function() {
  return "Hola";
};

Escribimos una función solo cuando la utilidad del código es más de una llamada.

Supongamos que queremos aumentar la utilidad de la función a un archivo diferente, digamos World.js, en este caso, la exportación de un archivo aparece en una imagen que puede obtenerse mediante module.exports.

Puede exportar tanto la función por el código que figura a continuación

var anyVariable={
 sayHelloInEnglish = function() {
      return "Hello";
    };
  sayHelloInSpanish = function() {
      return "Hola";
    }; 
}
module.export=anyVariable;

Ahora solo necesita solicitar el nombre del archivo en World.js para poder usar esas funciones

var world= require("./hello.js");
Shantanu Madane
fuente
Gracias Si te ha ayudado, acepta mi respuesta :)
Shantanu Madane
1
Un poco tarde para la fiesta amigo :)
Ben Taliadoros
@BenTaliadoros, yo también creo que llega tarde y también creo que su objeto anyVariable tiene muchos errores. la línea sobre el método sayHelloInSpanish no debe terminar con punto y coma (;) y sayHelloInSpanish = la función es incorrecta. Todas las cosas están mal con este objeto. editaré su respuesta
divina
1
La edición está deshabilitada. ¿Qué más editó alphadogg en esta respuesta?
divino
Solo formateando. A menos que sea una locura es6 que no he encontrado, y estoy seguro de que no lo es, entonces no es válido JS en absoluto
Ben Taliadoros
2

La intención es:

La programación modular es una técnica de diseño de software que enfatiza la separación de la funcionalidad de un programa en módulos independientes e intercambiables, de modo que cada uno contiene todo lo necesario para ejecutar solo un aspecto de la funcionalidad deseada.

Wikipedia

Me imagino que se hace difícil escribir programas grandes sin código modular / reutilizable. En nodejs podemos crear programas modulares utilizando la module.exportsdefinición de lo que exponemos y componimos nuestro programa require.

Prueba este ejemplo:

fileLog.js

function log(string) { require('fs').appendFileSync('log.txt',string); }

module.exports = log;

stdoutLog.js

function log(string) { console.log(string); }

module.exports = log;

program.js

const log = require('./stdoutLog.js')

log('hello world!');

ejecutar

$ nodo program.js

¡Hola Mundo!

Ahora intente intercambiar ./stdoutLog.js por ./fileLog.js .

Moriarty
fuente
1

¿Cuál es el propósito de un sistema de módulos?

Logra lo siguiente:

  1. Evita que nuestros archivos se hinchen a tamaños realmente grandes. Tener archivos con, por ejemplo, 5000 líneas de código suele ser muy difícil de manejar durante el desarrollo.
  2. Hace cumplir la separación de las preocupaciones. Tener nuestro código dividido en múltiples archivos nos permite tener nombres de archivo apropiados para cada archivo. De esta manera, podemos identificar fácilmente qué hace cada módulo y dónde encontrarlo (suponiendo que hayamos creado una estructura de directorio lógica que sigue siendo su responsabilidad).

Tener módulos hace que sea más fácil encontrar ciertas partes del código, lo que hace que nuestro código sea más fácil de mantener.

¿Como funciona?

NodejS utiliza el sistema de módulos CommomJS que funciona de la siguiente manera:

  1. Si un archivo quiere exportar algo, debe declararlo utilizando la module.exportsintaxis
  2. Si un archivo quiere importar algo, debe declararlo utilizando la require('file')sintaxis

Ejemplo:

test1.js

const test2 = require('./test2');    // returns the module.exports object of a file

test2.Func1(); // logs func1
test2.Func2(); // logs func2

test2.js

module.exports.Func1 = () => {console.log('func1')};

exports.Func2 = () => {console.log('func2')};

Otras cosas útiles para saber:

  1. Los módulos se están almacenando en caché . Cuando está cargando el mismo módulo en 2 archivos diferentes, el módulo solo debe cargarse una vez. La segunda vez que require()se llama a en el mismo módulo, se extrae del caché.
  2. Los módulos se cargan en síncrono . Este comportamiento es obligatorio, si fuera asíncrono no podríamos acceder al objeto recuperado de require()inmediato.
Willem van der Veen
fuente
-3
let test = function() {
    return "Hello world"
};
exports.test = test;
Gtm
fuente
44
Este es un ejemplo similar al del primer fragmento en la respuesta aceptada ( return "Hello world"no hace ninguna diferencia), pero sin ninguna explicación. Antes de responder, asegúrese de que su respuesta agregará algo al tema.
barbsan