第一次加载模块后对其进行缓存。这意味着(除其他事项外)每次对require('foo')的调用都将返回完全相同的对象(如果它可以解析为相同的文件)。使该缓存无效?即对于单元测试,我希望每个测试都可以在一个新对象上进行。
#1 楼
即使存在循环依赖关系,也始终可以安全地删除require.cache中的条目,而不会出现问题。因为在删除时,您只删除对缓存的模块对象的引用,而不是对模块对象本身的引用,所以不会对GC对象进行GC,因为在循环依赖的情况下,仍然有一个对象引用此模块对象。假设您拥有:
脚本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' }
===
如果直接运行node.js,则以上内容有效。但是,如果使用具有自己的模块缓存系统的工具(例如jest),则正确的语句将是:
jest.resetModules();
评论
您能否在初次要求b.js时解释为什么{... a:undefined}吗?我期望等于“来自a.js的a”。谢谢
–ira
17年8月7日在7:43
为什么是未定义的?
– Jeff P Chacko
17年8月15日在7:15
回复较晚,但是由于有循环依赖关系,因此我第一次收集到的b [a]是不确定的。 a.js需要b.js,而b.js又需要a.js。 a.js尚未完全加载,exports.a尚未定义,因此b.js一无所获。
–nik10110
17年9月24日在9:48
如果我按照此处所述使用require.main.require(path),有什么办法做到这一点? stackoverflow.com/questions/10860244/…
–舞狮
18 Mar 12 '18 at 17:06
感谢您在最后一段添加'jest.resetModules();',因为我在使用Mocha时遇到的大多数教程都非常有帮助!
–塞布
9月3日17:42
#2 楼
如果始终要重新加载模块,则可以添加以下功能:function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
,然后使用
requireUncached('./myModule')
代替require。评论
这与侦听文件更改的fs.watch方法完美结合。
–ph3nx
2014年7月12日在9:09
有什么风险?
–伤痕
18年11月11日在13:31
我有同样的问题,使用此解决方案而不是接受的答案有什么风险?
– Rotimi-best
19年2月11日,9:39
真的一样。根据代码的结构,当您尝试再次对其进行初始化时,可能会崩溃。例如模块是否启动服务器并侦听端口。下次您需要对模块进行未缓存时,它将失败,因为该端口已打开,依此类推。
–运气
19年2月11日在13:21
几乎完美的解决方案,但删除可能会很慢。
–user10398534
9月15日上午9:55
#3 楼
是的,您可以通过require.cache[moduleName]
访问缓存,其中moduleName
是您要访问的模块的名称。通过调用delete require.cache[moduleName]
删除条目将导致require
加载实际文件。这是删除与该模块关联的所有缓存文件的方式:
用法是:
/**
* 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));
}
};
由于此代码使用与
require
相同的解析程序,因此只需指定所需的内容即可。 > “ Unix并不是为了阻止其用户执行愚蠢的操作而设计的,因为
这也将阻止他们进行聪明的操作。” – Doug Gwyn
我认为应该有一种方法来执行显式的未缓存模块加载。
评论
+1仅用于道格的报价。我需要有人说出我也相信的东西:)
–波尼
13年3月17日在9:36
很好的答案!如果您想在启用重新加载的情况下启动节点repl,请查看此要点。
– gleitz
13-10-9在4:16
太棒了我会将其添加到require.uncache函数中。 ```//参见github.com/joyent/node/issues/8266 Object.keys(module.constructor._pathCache).forEach(function(k){if(k.indexOf(moduleName)> 0)删除module.constructor ._pathCache [k];});假设您需要一个模块,然后将其卸载,然后重新安装相同的模块,但使用了另一个版本,该版本的package.json中具有不同的主脚本,则下一个require将失败,因为该主脚本不存在,因为它缓存在Module._pathCache中
–bentael
2014年8月26日20:59
废话我的评论太糟糕了。我无法在此注释中整齐地添加代码,现在编辑为时已晚,所以我回答了。 @Ben Barkay,如果您可以编辑问题以向您的require.uncache添加一小段代码
–bentael
2014年8月26日在21:06
谢谢@bentael,我已将此添加到我的答案中。
– Ben Barkay
2014年8月28日14:43
#4 楼
有一个简单的模块(带有测试)我们在测试代码时遇到了这个确切的问题(删除缓存的模块,以便可以在新的状态下重新请求它们),因此我们回顾了人们对各种StackOverflow问题与解答进行讨论,并组装了一个简单的node.js模块(带有测试): >
如您所料,既可以用于已发布的npm软件包,也可以用于本地定义的模块。 Windows,Mac,Linux等。
如何? (用法)
用法非常简单:
install
从npm安装模块:
npm install decache --save-dev
在您的代码中使用它:
// require the decache module:
const decache = require('decache');
// require a module that you wrote"
let 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)
如果有任何疑问或需要更多示例,请创建一个GitHub问题:
https ://github.com/dwyl/decache/issues
评论
我一直在研究它,它在测试时非常适合我使用,以便我可以在特定条件下卸载和重新加载模块,但是不幸的是,我在工作,我的公司避开了GPL许可证。我只想将其用于测试,所以我仍在考虑它,因为它看起来很有帮助。
– Matt_JD
16年9月9日在7:28
@Matt_JD感谢您的反馈。您想要哪个许可证?
–nelsonic
16 Mar 9 '16 at 14:13
@Matt_JD我们已将许可证更新为MIT。祝您工作顺利! :-)
–nelsonic
16 Mar 9 '16 at 22:39
这很棒!为此回购加注星标,并对该答案进行投票。
– aholt
18年11月13日在22:41
强烈建议,从今天开始在最新的v14.2.0上运行良好
–汤姆
5月10日17:56
#5 楼
对于使用Jest的任何人,因为Jest进行自己的模块缓存,所以有一个内置功能-只需确保jest.resetModules
运行即可。在每个测试之后:感谢Anthony Garvan。此处的功能文档。
评论
非常感谢您的来信!
–mjgpy3
19/12/4在20:57
天哪,我做了多久才发现...。谢谢!
– Tiago
19/12/5在15:12
#6 楼
解决方案是使用以下方法:假设您在目录的根目录中有一个虚拟的
example.js
文件: 如果然后在
require()
中添加这样的行: /> delete require.cache[require.resolve(<path of your script>)]
那是当您可以使用luff答案中指出的
example.js
的时候: delete require.cache[require.resolve()]
再次捕获文件的内容,并加载所有当前值。评论
恕我直言,这是最合适的答案
– Piyush Katariya
18-10-26在8:14
#7 楼
rewire非常适合此用例,每次调用都会获得一个新实例。轻松的依赖注入,可用于node.js单元测试。您可以为其他模块或全局变量(例如进程)注入模拟
泄漏私有变量
覆盖模块中的变量。
rewire不会加载文件并评估内容模拟节点的require机制。实际上,它使用节点自身的要求来加载模块。因此,您的模块在测试环境中的行为与正常情况下完全相同(修改后除外)。请注意,在这种情况下,需要在您的devDependencies中列出CoffeeScript。
#8 楼
我会再增加一行,并更改参数名称: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)
}
评论
因此,这是为了使功能在子模块中工作?真好!从module.children数组中删除模块的较短方法是使用过滤器功能:module.children = module.children.filter(function(child){return child.id!== require.resolve(_module);}) ;
–运气
2014年4月6日15:09
#9 楼
是的,您可以使缓存无效。缓存存储在名为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
#10 楼
我无法在答案的注释中整齐地添加代码。但是我会使用@Ben Barkay的答案,然后将其添加到require.uncache
函数中。但使用的package.json中包含不同主脚本的其他版本,下一个需求将失败,因为该主脚本不存在,因为该主脚本已缓存在Module._pathCache
中#11 楼
我不确定100%的“无效”是什么意思,但是您可以在require
语句上方添加以下内容以清除缓存:Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })
来自@ Dancrumb在这里的评论
#12 楼
requireUncached
带有相对路径:🔥const requireUncached = require => module => {
delete require.cache[require.resolve(module)];
return require(module);
};
module.exports = requireUncached;
调用requireUncached带有相对路径:
const requireUncached = require('../helpers/require_uncached')(require);
const myModule = requireUncached('./myModule');
#13 楼
以下两步过程对我来说是完美的。动态更改
Model
文件(即'mymodule.js'
)后,需要先删除猫鼬模型中的预编译模型,然后使用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');
#14 楼
文档说:在需要时将模块缓存在此对象中。通过从该对象中删除键值,下一个require将重新加载模块。这不适用于本机加载项,重新加载本机加载项会导致错误。
#15 楼
如果您只希望模块永远不被缓存(有时对开发很有用,但是记住在完成后将其删除!),只需将delete require.cache[module.id];
放入模块中即可。#16 楼
如果用于单元测试,则另一个有用的工具是proxyquire。每次您代理查询模块时,它将使模块缓存无效并缓存一个新的模块。它还允许您修改要测试的文件所需的模块。#17 楼
我做了一个小模块,以便在加载后从缓存中删除模块。这将在下次需要时重新评估模块。参见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
PS:您也可以将“自毁”放入模块本身。参见https://github.com/bahmutov/unload-me
PSS:我的https://glebbahmutov.com/blog/hacking-node-require/
评论
NPM模块github.com/gajus/require-new另一个带有观察程序的NPM模块:npmjs.com/package/updated-require
可以在不使用require的情况下缓存文件内容,并将其评估为不同的作用域stackoverflow.com/questions/42376161/…