Comprender los módulos de Node.js: ¿varios requieren devolver el mismo objeto?


106

Tengo una pregunta relacionada con la documentación de node.js sobre el almacenamiento en caché del módulo :

Los módulos se almacenan en caché después de la primera vez que se cargan. Esto significa (entre otras cosas) que cada llamada a require ('foo') obtendrá exactamente el mismo objeto devuelto, si se resuelve en el mismo archivo.

Es posible que varias llamadas a require ('foo') no hagan que el código del módulo se ejecute varias veces. Esta es una característica importante. Con él, se pueden devolver objetos "parcialmente hechos", lo que permite que se carguen las dependencias transitivas incluso cuando causarían ciclos.

¿A qué se refiere may?

Quiero saber si require siempre devolverá el mismo objeto. Así que en caso requiero un módulo A en app.jsy cambiar el objeto de exportaciones dentro app.js(la que requiere retornos) y después de que requieren un módulo B en el app.jsque sí requiere módulo A , voy siempre obtener la versión modificada de ese objeto, o un nuevo ¿uno?

// app.js

var a = require('./a');
a.b = 2;
console.log(a.b); //2

var b = require('./b');
console.log(b.b); //2

// a.js

exports.a = 1;

// b.js

module.exports = require('./a');

7
Esa oración en los documentos podría haber estado mejor escrita. Me parece que puede que no sea ​​lo mismo que no está permitido , es decir, varias llamadas a require ('foo') no pueden hacer que el código del módulo se ejecute varias veces .
— Lucio Paiva

@LucioPaiva Creó un PR para solucionarlo: github.com/nodejs/node/pull/23143
— mikemaccana

Querían decir [[no puede] causar], en contraposición a [[puede] no causar], en el mismo sentido que "No, Timmy, no puedes comer más chocolate". Entonces, en este contexto, estoy de acuerdo, está escrito de manera ambigua.
— Ciabaros

Respuestas:


76

Si ambos app.jsy b.jsresiden en el mismo proyecto (y en el mismo directorio), ambos recibirán la misma instancia de A. De la documentación de node.js :

... cada llamada a require('foo')obtendrá exactamente el mismo objeto devuelto, si se resuelve en el mismo archivo .


La situación es diferente cuando a.js, b.jsy app.jsestán en diferentes módulos de la NGP . Por ejemplo:

[APP] --> [A], [B]
[B]   --> [A]

En ese caso, el require('a')en app.jsresolvería a una copia diferente del a.jsque require('a')en b.jsy por lo tanto devolver una instancia diferente de A. Hay una publicación de blog que describe este comportamiento con más detalle.


1
"y en el mismo directorio". Recibí la misma instancia cuando [B] estaba en una subcarpeta de donde vivía [App].
— Bill Tarbell

1
@BillTarbell Cuando quería obtener la misma instancia de diferentes carpetas, obtuve dos diferentes.
— MustSeeMelons

1
¿Esta última advertencia sigue siendo cierta ahora que npm instalaría A, B y APP en una estructura de directorio plana? Si no es así, ¿cómo se puede configurar un módulo para almacenar en caché sus resultados entre varios otros módulos?
— Michael

De la documentación de node.js: "Los módulos se almacenan en caché en función de su nombre de archivo resuelto. Dado que los módulos pueden resolverse en un nombre de archivo diferente según la ubicación del módulo de llamada (cargando desde las carpetas de node_modules), no es una garantía que requiera ('foo ') siempre devolverá exactamente el mismo objeto, si se resuelve en archivos diferentes ". Por lo que puede no utilizar la caché requieren para implementar un comportamiento único, sistemas de archivos caso insensible o enlaces simbólicos y tal puede causar el mismo módulo que va a cargar varias veces. Si necesita datos globales, debe utilizar el objeto "global".
— Tanino

6

node.js tiene algún tipo de almacenamiento en caché implementado que bloquea al nodo para que no lea archivos miles de veces mientras ejecuta algunos proyectos de servidor enormes.

Esta caché se enumera en el require.cacheobjeto. Tengo que señalar que este objeto es de lectura / escritura, lo que permite eliminar archivos de la memoria caché sin matar el proceso.

http://nodejs.org/docs/latest/api/globals.html#require.cache

Ouh, olvidé responder la pregunta. La modificación del objeto exportado no afecta la siguiente carga del módulo. Esto causaría muchos problemas ... Requerir siempre devolver una nueva instancia del objeto, sin referencia. La edición del archivo y la eliminación de la caché cambia el objeto exportado

Después de hacer algunas pruebas, node.js almacena en caché el module.exports. La modificación require.cache[{module}].exportstermina en un objeto devuelto nuevo y modificado.


1
El código que publiqué realmente funciona. b.bse define con el valor 2. Entonces, en este ejemplo, es el mismo objeto.
— Xomby

Esa es una característica y, en mi opinión, bastante útil. La pregunta es si puedo confiar en ello. La documentación dice maylo que lo hace confuso.
— Xomby

La ejecución de una función en require en a.js no cambia nada ... Sigue almacenando en caché el objeto devuelto. Extraño, ¿cómo funcionan mis aplicaciones? La única forma es, como se describe en los documentos, ejecutar una función pasada después de require.
— moe

1
a veces los objetos son los mismos. No confíe en eso, pase una sola instancia de módulo si es necesario.
— Ricardo Tomasi

¿Hay alguna forma de recuperar un objeto nuevo en un requisito (no el objeto en caché)?
— Matt

3

Desde que se publicó la pregunta, el documento se ha actualizado para aclarar por qué se usó originalmente "may". Ahora responde la pregunta en sí misma haciendo las cosas explícitas (mi énfasis es mostrar lo que ha cambiado):

Los módulos se almacenan en caché después de la primera vez que se cargan. Esto significa (entre otras cosas) que cada llamada a require ('foo') obtendrá exactamente el mismo objeto devuelto, si se resuelve en el mismo archivo.

Siempre que require.cache no se modifique, varias llamadas a require ('foo') será no hacer que el código del módulo que se ejecuta varias veces. Esta es una característica importante. Con él, se pueden devolver objetos "parcialmente hechos", lo que permite que se carguen las dependencias transitivas incluso cuando causarían ciclos.


2

Por lo que he visto, si el nombre del módulo se resuelve en un archivo previamente cargado, se devolverá el módulo en caché; de lo contrario, el nuevo archivo se cargará por separado.

Es decir, el almacenamiento en caché se basa en el nombre de archivo real que se resuelve. Esto se debe a que, en general, puede haber diferentes versiones del mismo paquete que se instalan en diferentes niveles de la jerarquía de archivos y que deben cargarse en consecuencia.

Lo que no estoy seguro es si hay casos de invalidación de caché que no están bajo el control o conocimiento del programador, que podrían hacer posible recargar accidentalmente el mismo archivo de paquete varias veces.


1

En caso de que la razón por la que desee require(x)devolver un objeto nuevo cada vez sea solo porque modifica ese objeto directamente, que es un caso con el que me encontré, simplemente clónelo, modifique y use solo el clon, así:

var a = require('./a');
a = JSON.parse(JSON.stringify(a));

0

prueba drex : https://github.com/yuryb/drex

drex está mirando un módulo en busca de actualizaciones y claramente vuelve a requerir el módulo después de la actualización. El nuevo código se requiere () d como si el nuevo código fuera un módulo totalmente diferente, por lo que require.cache no es un problema.


2
La pregunta era otra, pero drex se ve realmente genial para el desarrollo. ¡Gracias!
— Alex Ivasyuv

0

Cuando necesita un objeto, está solicitando su dirección de referencia y, al solicitar el objeto dos veces, obtendrá la misma dirección. Para tener copias del mismo objeto, debe copiarlo (clonarlo).

var obj = require('./obj');

a = JSON.parse(JSON.stringify(obj));
b = JSON.parse(JSON.stringify(obj));
c = JSON.parse(JSON.stringify(obj));

La clonación se realiza de varias formas, puede ver esto para obtener más información.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.