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。 有了它,“部分完成”的对象可以被返回,从而允许传递依赖被加载,即使它们会导致循环。
如果你想要一个模块多次执行代码,那么导出一个函数,然后调用这个函数。
这里有两点:
-
这是必要的原因是让循环得到解决。 你可以在这里看到一个例子: http : //nodejs.org/docs/latest/api/modules.html#modules_cycles 。 如果你可以以某种方式使caching无效,那么由于循环依赖性,你可能会导致无限循环。 即使您可以合理地确信您的应用程序代码不会造成这种情况,也可能发生在您使用的任何库中。
-
正如文档指出的那样,您可以将function封装在一个可以在每个testing中调用的函数中。 这通常也是一个相当不错的devise模式。
有一个简单的模块 ( 与testing )
我们在testing代码的时候遇到了这个确切的问题( 删除caching的模块,以便在新的状态下可以重新使用它们 ),所以我们回顾了各种删除caching的require()
( 包括npm包和本地定义的模块 )相关的StackOverflow问题和放在一起简单的 节点/ io.js模块 ( 与testing ):
https://www.npmjs.com/package/ decache
怎么样? ( 用法 )
用法很简单:
安装
从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'];
我们的团队发现以下模块有用。 使某些模块组无效。
我会再多加一个行的答案,并更改参数名称:
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文档在这里 。