node.js require()caching – 可能失效?

从node.js文档:

模块在第一次加载后被caching。 这意味着(除其他外)每个调用require('foo')将返回完全相同的对象,如果它将parsing为相同的文件。

有没有办法使这个caching无效? 即对于unit testing,我希望每个testing都是在一个新的对象上工作。

即使存在循环依赖关系,也可以安全地删除require.cache中的条目,而不会出现问题。 因为当你删除的时候,你只需要删除一个对caching模块对象的引用,而不是模块对象本身,那么模块对象将不会被GCed,因为在循环依赖的情况下,仍然有一个引用这个模块对象的对象。 假设你有一个脚本a.js:

var b=require('./b.js').b; exports.a='a from a.js'; exports.b=b; 

和一个脚本b.js:

 var a=require('./a.js').a; exports.b='b from b.js'; exports.a=a; 

当你这样做时:

 var a=require('./a.js') var b=require('./b.js') 

你会得到:

 > a { a: 'a from a.js', b: 'b from b.js' } > b { b: 'b from b.js', a: undefined } 

现在如果你编辑你的b.js:

 var a=require('./a.js').a; exports.b='b from b.js. changed value'; exports.a=a; 

并做:

 delete require.cache[require.resolve('./b.js')] b=require('./b.js') 

你会得到:

 > a { a: 'a from a.js', b: 'b from b.js' } > b { b: 'b from b.js. changed value', a: 'a from a.js' } 

是的,您可以通过require.cache[moduleName]访问caching,其中moduleName是您希望访问的模块的名称。 通过调用delete require.cache[moduleName]删除一个条目将导致require加载实际的文件。

这是你将如何删除与模块相关的所有caching文件:

 /** * 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)); } }; 

用法是:

 // Load the package var mypackage = require('./mypackage'); // Purge the package from cache purgeCache('./mypackage'); 

由于此代码使用相同的parsing器require ,只需指定任何你想要的。


“Unix并不是为了阻止用户做愚蠢的事情,因为这样做也会阻止他们做出聪明的事情。” – Doug Gwyn

我认为应该有一个方法来执行明确的未caching模块加载。

如果你总是想重新加载你的模块,你可以添加这个函数:

 function requireUncached(module){ delete require.cache[require.resolve(module)] return require(module) } 

然后使用requireUncached('./myModule')而不是require。 当然,风险自负。

编辑:

我纠正了。 正如seppo0010指出的那样,您可以通过从require.cache删除caching的模块来强制重新加载: http : require.cache

这就是说,我仍然build议不要这样做,原因如下所述。 再说一遍,如果你只是在你的unit testing层做的话,你可能会发出吱吱声,没有任何无限的依赖链。


原来的答案

不,没有办法做到这一点。 另外从文档:

多次调用require('foo')可能不会导致模块代码被多次执行。 这是一个重要的function。 有了它,“部分完成”的对象可以被返回,从而允许传递依赖被加载,即使它们会导致循环。

如果你想要一个模块多次执行代码,那么导出一个函数,然后调用这个函数。

这里有两点:

  1. 这是必要的原因是让循环得到解决。 你可以在这里看到一个例子: http : //nodejs.org/docs/latest/api/modules.html#modules_cycles 。 如果你可以以某种方式使caching无效,那么由于循环依赖性,你可能会导致无限循环。 即使您可以合理地确信您的应用程序代码不会造成这种情况,也可能发生在您使用的任何库中。

  2. 正如文档指出的那样,您可以将function封装在一个可以在每个testing中调用的函数中。 这通常也是一个相当不错的devise模式。

有一个简单的模块与testing

我们在testing代码的时候遇到了这个确切的问题( 删除caching的模块,以便在新的状态下可以重新使用它们 ),所以我们回顾了各种删除caching的require()包括npm包和本地定义的模块 )相关的StackOverflow问题和放在一起简单的 节点/ io.js模块与testing ):

https://www.npmjs.com/package/ decache

构建状态 测试覆盖率 守则气候 依赖状态 devDependency状态

怎么样? ( 用法

用法很简单:

安装

从npm安装模块:

npm install decache --save-dev

在你的代码中使用它:

 // 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) 

如果您有任何问题或需要更多示例,请创build一个GitHub问题: https : //github.com/dwyl/decache/issues

解决scheme是使用:

 delete require.cache[require.resolve(<path of your script>)] 

对于像我这样的人,在这里find一些基本的解释:

假设在你的目录的根目录下有一个dummy example.js文件:

 exports.message = "hi"; exports.say = function () { console.log(message); } 

那么你require()像这样:

 $ node > require('./example.js') { message: 'hi', say: [Function] } 

如果你然后像这样添加一行到example.js

 exports.message = "hi"; exports.say = function () { console.log(message); } exports.farewell = "bye!"; // this line is added later on 

并继续在控制台中,模块不更新:

 > require('./example.js') { message: 'hi', say: [Function] } 

那么当你可以使用delete require.cache[require.resolve()]在路夫的答案中指出:

 > delete require.cache[require.resolve('./example.js')] true > require('./example.js') { message: 'hi', say: [Function], farewell: 'bye!' } 

所以清理caching, require()再次捕获文件的内容,加载所有的当前值。

rewire对于这个用例来说非常棒,每次调用都会得到一个新的实例。 易于dependency injectionnode.jsunit testing。

rewire增加了一个特殊的setter和getter模块,所以你可以修改他们的行为,以更好的unit testing。 你可以

为其他模块或全局variables注入模拟,如进程泄漏私有variables覆盖模块中的variables。 重新连接不加载文件并评估内容以模拟节点的需求机制。 实际上它使用节点自己的要求来加载模块。 因此,您的模块在testing环境中的行为与正常情况下的行为完全相同(除了您的修改)。

所有的咖啡因成瘾者的好消息:重新连接咖啡脚本的作品。 请注意,在这种情况下,CoffeeScript需要列在devDependencies中。

是的,你可以使caching无效。

caching存储在一个名为require.cache的对象中,您可以根据文件名直接访问对象(例如 – /projects/app/home/index.js ,而不是./home ,您可以在require('./home')声明)。

 delete require.cache['/projects/app/home/index.js']; 

我们的团队发现以下模块有用。 使某些模块组无效。

https://www.npmjs.com/package/node-resource

我会再多加一个行的答案,并更改参数名称:

 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) } 

我无法在答案评论中整齐地添加代码。 但是,我会使用@Ben Barkay的答案,然后将其添加到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 ]; } }); 

假设你需要一个模块,然后卸载它,然后重新安装相同的模块,但使用不同的版本,在package.json中有一个不同的主脚本,下一个需求将失败,因为主脚本不存在,因为它被cachingModule._pathCache

以下两步程序是完美的为我工作。

在dynamic更改Model文件(即'mymodule.js' ,您需要先删除mongoose模型中的预编译模型,然后使用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'); 

如果是unit testing,另一个好的工具是使用proxyquire 。 每当你使用模块,就会使模块caching失效并caching一个新的caching。 它还允许您修改正在testing的文件所需的模块。

对于任何使用Jest的人来说,因为Jest有自己的模块caching,所以这里有一个内置的函数 – 只要确保jest.resetModules运行例如。 在你的每个testing之后:

 afterEach( function() { jest.resetModules(); }); 

试图像使用另一个答案build议使用decache之后find了这个。 感谢Anthony Garvan 。

function文档在这里 。