node.js - modules - require node js
node.js require() cache-¿Es posible invalidar? (14)
De la documentación de node.js:
Los módulos se almacenan en caché después de la primera vez que se cargan. Esto significa (entre otras cosas) que cada llamada que requiera (''foo'') obtendrá exactamente el mismo objeto devuelto, si se resolviera en el mismo archivo.
¿Hay una manera de invalidar este caché? es decir, para pruebas unitarias, me gustaría que cada prueba esté trabajando en un objeto nuevo.
Hay un módulo simple para eso ( con pruebas )
Tuvimos este problema exacto al probar nuestro código ( eliminar los módulos en caché para que puedan volver a requerirse en un estado nuevo ), por lo que revisamos todas las sugerencias de las personas en las diversas Preguntas y respuestas de y creamos un módulo simple de node.js ( con pruebas ):
Como es de esperar, funciona tanto para los paquetes npm publicados como para los módulos definidos localmente . Windows, Mac, Linux, etc.
¿Cómo? ( uso )
El uso es bastante simple:
instalar
Instale el módulo desde npm:
npm install decache --save-dev
Úsalo en tu código:
// require the decache module:
var decache = require(''decache'');
// require a module that you wrote"
var mymod = require(''./mymodule.js'');
// use your module the way you need to:
console.log(mymod.count()); // 0 (the initial state for our counter is zero)
console.log(mymod.incrementRunCount()); // 1
// delete the cached module:
decache(''./mymodule.js'');
//
mymod = require(''./mymodule.js''); // fresh start
console.log(mymod.count()); // 0 (back to initial state ... zero)
Si tiene alguna pregunta o necesita más ejemplos, cree un problema de GitHub: https://github.com/dwyl/decache/issues
Agregaría a la línea de respuesta de luff una línea más y cambiaría el nombre del parámetro:
function requireCached(_module){
var l = module.children.length;
for (var i = 0; i < l; i++)
{
if (module.children[i].id === require.resolve(_module))
{
module.children.splice(i, 1);
break;
}
}
delete require.cache[require.resolve(_module)];
return require(_module)
}
Hice un pequeño módulo para eliminar el módulo de la memoria caché después de cargar. Esto obliga a la reevaluación del módulo la próxima vez que se requiera. Consulte https://github.com/bahmutov/require-and-forget
// random.js
module.exports = Math.random()
const forget = require(''require-and-forget'')
const r1 = forget(''./random'')
const r2 = forget(''./random'')
// r1 and r2 will be different
// "random.js" will not be stored in the require.cache
PD: también puedes poner "autodestrucción" en el propio módulo. Ver https://github.com/bahmutov/unload-me
PSS: más trucos con Nodo requieren en mi https://glebbahmutov.com/blog/hacking-node-require/
La solución es utilizar:
delete require.cache[require.resolve(<path of your script>)]
Encuentre aquí algunas explicaciones básicas para aquellos que, como yo, son un poco nuevos en esto:
Supongamos que tiene un archivo example.js
ficticio en la raíz de su directorio:
exports.message = "hi";
exports.say = function () {
console.log(message);
}
Entonces require()
así:
$ node
> require(''./example.js'')
{ message: ''hi'', say: [Function] }
Si luego agrega una línea como esta a example.js
:
exports.message = "hi";
exports.say = function () {
console.log(message);
}
exports.farewell = "bye!"; // this line is added later on
Y continuar en la consola, el módulo no se actualiza:
> require(''./example.js'')
{ message: ''hi'', say: [Function] }
Ahí es cuando puede usar delete require.cache[require.resolve()]
indicado en la respuesta de luff :
> delete require.cache[require.resolve(''./example.js'')]
true
> require(''./example.js'')
{ message: ''hi'', say: [Function], farewell: ''bye!'' }
Así que el caché se limpia y el require()
vuelve a capturar el contenido del archivo, cargando todos los valores actuales.
No pude agregar código en el comentario de una respuesta. Pero usaría la respuesta de @Ben Barkay y luego agregaría esto a la función require.uncache
.
// see https://github.com/joyent/node/issues/8266
// use in it in @Ben Barkay''s require.uncache function or along with it. whatever
Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
if ( cacheKey.indexOf(moduleName) > -1 ) {
delete module.constructor._pathCache[ cacheKey ];
}
});
Digamos que ha requerido un módulo, luego lo desinstaló, luego reinstaló el mismo módulo pero usó una versión diferente que tiene un script principal diferente en su package.json, el siguiente requisito fallará porque ese script principal no existe porque está en caché Module._pathCache
Para cualquiera que se encuentre con este que está usando Jest, porque Jest hace su propio almacenamiento en caché de módulos, hay una función incorporada para esto, solo asegúrese de que jest.resetModules
ejecute, por ejemplo. Después de cada una de tus pruebas:
afterEach( function() {
jest.resetModules();
});
Encontré esto después de intentar usar el https://www.npmjs.com/package/decache como otra respuesta sugerida. Gracias a Anthony Garvan .
Documentación de la función here .
Sí, puede acceder al caché a través de require.cache[moduleName]
donde moduleName
es el nombre del módulo al que desea acceder. Eliminar una entrada llamando a delete require.cache[moduleName]
hará que se require
cargar el archivo real.
Así es como eliminaría todos los archivos en caché asociados con el módulo:
/**
* Removes a module from the cache
*/
function purgeCache(moduleName) {
// Traverse the cache looking for the files
// loaded by the specified module name
searchCache(moduleName, function (mod) {
delete require.cache[mod.id];
});
// Remove cached paths to the module.
// Thanks to @bentael for pointing this out.
Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
if (cacheKey.indexOf(moduleName)>0) {
delete module.constructor._pathCache[cacheKey];
}
});
};
/**
* Traverses the cache to search for all the cached
* files of the specified module name
*/
function searchCache(moduleName, callback) {
// Resolve the module identified by the specified name
var mod = require.resolve(moduleName);
// Check if the module has been resolved and found within
// the cache
if (mod && ((mod = require.cache[mod]) !== undefined)) {
// Recursively go over the results
(function traverse(mod) {
// Go over each of the module''s children and
// traverse them
mod.children.forEach(function (child) {
traverse(child);
});
// Call the specified callback providing the
// found cached module
callback(mod);
}(mod));
}
};
El uso sería:
// Load the package
var mypackage = require(''./mypackage'');
// Purge the package from cache
purgeCache(''./mypackage'');
Dado que este código utiliza la misma resolución que require
, solo especifique lo que necesita.
"Unix no fue diseñado para evitar que sus usuarios hicieran cosas estúpidas, ya que eso también evitaría que hicieran cosas inteligentes". - Doug Gwyn
Creo que debería haber una manera de realizar una carga explícita de módulos sin caché.
Sí, puedes invalidar el caché.
El caché se almacena en un objeto llamado require.cache al que puede acceder directamente de acuerdo con los nombres de archivo (por ejemplo, - /projects/app/home/index.js
en lugar de ./home
que usaría en un require(''./home'')
declaración).
delete require.cache[''/projects/app/home/index.js''];
Nuestro equipo ha encontrado útil el siguiente módulo. Para invalidar ciertos grupos de módulos.
Si es para pruebas unitarias, otra buena herramienta para usar es proxyquire . Cada vez que solicite el módulo, esto invalidará la memoria caché del módulo y la memoria caché una nueva. También le permite modificar los módulos requeridos por el archivo que está probando.
Si siempre quieres volver a cargar tu módulo, puedes agregar esta función:
function requireUncached(module){
delete require.cache[require.resolve(module)]
return require(module)
}
y luego use requireUncached(''./myModule'')
lugar de require. A su propio riesgo, por supuesto.
Siempre puede eliminar de forma segura una entrada en require.cache sin problemas, incluso cuando existen dependencias circulares. Debido a que cuando elimina, simplemente elimina una referencia al objeto de módulo en caché, no al objeto de módulo en sí, el objeto de módulo no será controlado porque en caso de dependencias circulares, todavía hay un objeto que hace referencia a este objeto de módulo.
Supongamos que usted tiene:
script a.js:
var b=require(''./b.js'').b;
exports.a=''a from a.js'';
exports.b=b;
y script b.js:
var a=require(''./a.js'').a;
exports.b=''b from b.js'';
exports.a=a;
Cuando tu lo hagas:
var a=require(''./a.js'')
var b=require(''./b.js'')
conseguirás:
> a
{ a: ''a from a.js'', b: ''b from b.js'' }
> b
{ b: ''b from b.js'', a: undefined }
Ahora si editas tu b.js:
var a=require(''./a.js'').a;
exports.b=''b from b.js. changed value'';
exports.a=a;
y hacer:
delete require.cache[require.resolve(''./b.js'')]
b=require(''./b.js'')
conseguirás:
> a
{ a: ''a from a.js'', b: ''b from b.js'' }
> b
{ b: ''b from b.js. changed value'',
a: ''a from a.js'' }
rewire es ideal para este caso de uso, usted obtiene una nueva instancia con cada llamada. Fácil inyección de dependencias para pruebas de la unidad node.js.
rewire agrega un setter y getter especial a los módulos para que pueda modificar su comportamiento para una mejor prueba de la unidad. Puedes
inyectar simulacros para otros módulos o elementos globales como las variables privadas de pérdida de proceso anulan las variables dentro del módulo. rewire no carga el archivo y evalúa el contenido para emular el mecanismo de requerimiento del nodo. De hecho, utiliza el propio requerimiento del nodo para cargar el módulo. Por lo tanto, su módulo se comporta exactamente igual en su entorno de prueba que en circunstancias normales (excepto sus modificaciones).
Buenas noticias para todos los adictos a la cafeína: Rewire también funciona con Coffee-Script. Tenga en cuenta que, en este caso, CoffeeScript debe aparecer en sus Dependencias de desarrollo.
EDITAR:
Me quedo corregido. Como ha señalado seppo0010 , puede forzar una recarga eliminando el módulo en caché de require.cache
: http://nodejs.org/docs/latest/api/globals.html#globals_require_cache
Dicho esto, todavía recomendaría no hacer esto por las razones que se mencionan a continuación. Por otra parte, si solo lo está haciendo en su capa de prueba unitaria, es posible que pueda pasar por alto sin ninguna cadena de dependencia infinita.
respuesta original :
No, realmente no hay manera de hacer esto. También a partir de la documentación:
Las llamadas múltiples que requieren (''foo'') pueden no hacer que el código del módulo se ejecute varias veces. Esta es una característica importante. Con él, los objetos "parcialmente hechos" se pueden devolver, lo que permite que se carguen las dependencias transitivas incluso cuando causan ciclos.
Si desea que un módulo ejecute el código varias veces, exporte una función y llame a esa función.
Dos puntos aquí:
La razón por la que esto es necesario es para permitir que los ciclos se resuelvan. Puede ver un ejemplo de esto aquí: http://nodejs.org/docs/latest/api/modules.html#modules_cycles . Si pudiera invalidar el caché de alguna manera, podría causar un bucle infinito debido a las dependencias circulares. Incluso si puede estar razonablemente seguro de que el código de su aplicación no causará esto, podría suceder en cualquier biblioteca que use.
Como lo señala la documentación, puede ajustar la funcionalidad en una función a la que puede llamar en cada prueba. Este es generalmente un patrón de diseño bastante bueno también.
El siguiente procedimiento de dos pasos funciona perfectamente para mí.
Después de cambiar el archivo de Model
, es decir, ''mymodule.js''
dinámicamente, ''mymodule.js''
debe eliminar el modelo precompilado en el modelo de mongoose y luego volver a cargarlo utilizando require-reload
Example:
// Delete mongoose model
delete mongoose.connection.models[thisObject.singular(''mymodule'')]
// Reload model
var reload = require(''require-reload'')(require);
var entityModel = reload(''./mymodule.js'');