来自node.js文档:
模块在第一次加载后进行缓存。这意味着(除其他外)每次调用require('foo')将获得完全相同的返回对象,如果它将解析为同一个文件。
有没有办法让这个缓存无效?即对于单元测试,我希望每个测试都能处理新的物体。
答案 0 :(得分:267)
即使存在循环依赖关系,您也可以安全地删除require.cache中的条目而不会出现问题。因为删除时只删除对缓存模块对象的引用,而不是模块对象本身,模块对象将不会被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' }
答案 1 :(得分:135)
如果您总是想重新加载模块,可以添加此功能:
function requireUncached(module){
delete require.cache[require.resolve(module)]
return require(module)
}
然后使用requireUncached('./myModule')
代替require。
答案 2 :(得分:126)
是的,您可以通过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));
}
};
用法是:
// Load the package
var mypackage = require('./mypackage');
// Purge the package from cache
purgeCache('./mypackage');
由于此代码使用相同的解析器require
,因此只需指定您需要的任何内容。
“Unix并不是为了阻止用户做蠢事,因为 这也会阻止他们做出聪明的事情。“ - Doug Gwyn
我认为应该是一种执行显式未缓存模块加载的方法。
答案 3 :(得分:29)
我们在测试我们的代码(删除缓存模块以便在新状态下重新需要)时遇到了这个问题,所以我们审核了所有关于各种StackOverflow问题和人们的建议回答并整理简单 node.js模块 ( with tests ):
正如您所料,两者发布了npm包和本地定义的模块。 Windows,Mac,Linux等
用法非常简单:
从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)
如果您有任何疑问或需要更多示例,请创建一个GitHub问题: https://github.com/dwyl/decache/issues
答案 4 :(得分:14)
解决方案是使用:
delete require.cache[require.resolve(<path of your script>)]
在这里找到一些基本的解释,对于那些像我一样有点新鲜的人:
假设您的目录根目录中有一个虚拟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] }
那时你可以使用luff's answer中指明的delete require.cache[require.resolve()]
:
> delete require.cache[require.resolve('./example.js')]
true
> require('./example.js')
{ message: 'hi', say: [Function], farewell: 'bye!' }
因此清理缓存并再次require()
捕获文件的内容,加载所有当前值。
答案 5 :(得分:8)
对于遇到这个使用Jest的人来说,因为Jest有自己的模块缓存,所以有一个内置函数 - 只需确保jest.resetModules
运行,例如。在每次测试之后:
afterEach( function() {
jest.resetModules();
});
在尝试使用decache之后找到这个,就像建议的另一个答案一样。感谢Anthony Garvan。
功能文档here。
答案 6 :(得分:5)
rewire非常适合这个用例,每次调用都会获得一个新实例。 node.js单元测试的简易依赖注入。
rewire为模块添加了一个特殊的setter和getter,因此您可以修改它们的行为以进行更好的单元测试。你可以
为其他模块注入模拟,或者为进程注入全局数据 泄漏私有变量 覆盖模块中的变量。 重新连接不会加载文件并评估内容以模拟节点的需求机制。实际上它使用节点自己的require来加载模块。因此,您的模块在测试环境中的行为与在常规情况下(除了您的修改)完全相同。
所有咖啡因成瘾者的好消息:重新连线也适用于Coffee-Script。请注意,在这种情况下,需要在devDependencies中列出CoffeeScript。
答案 7 :(得分:4)
是的,您可以使缓存无效。
缓存存储在名为require.cache的对象中,您可以根据文件名直接访问该对象(例如 - /projects/app/home/index.js
,而不是./home
语句中使用的require('./home')
)。
delete require.cache['/projects/app/home/index.js'];
我们的团队发现以下模块很有用。使某些模块组无效。
答案 8 :(得分:3)
我无法在答案的评论中整齐地添加代码。但我会使用@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中包含不同的主脚本,下一个需要将失败,因为主脚本不存在,因为它被缓存在Module._pathCache
答案 9 :(得分:3)
我会再向luff添加一行代码并更改参数名称:
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)
}
答案 10 :(得分:3)
我不确定100%的“无效”是什么意思,但是您可以在require
语句上方添加以下内容以清除缓存:
Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })
来自@Dancrumb的评论here
答案 11 :(得分:2)
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');
答案 12 :(得分:1)
以下两步程序对我来说非常有效。
动态更改Model
档案i-e 'mymodule.js'
后,您需要先在mongoose model中删除预编译的模型,然后使用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');
答案 13 :(得分:1)
在需要时将模块缓存在此对象中。 通过从该对象中删除键值,下一个require将重新加载模块。这不适用于本机插件,重新加载将导致错误。
答案 14 :(得分:1)
如果您只希望模块永远不被缓存(有时对开发很有用,但是请记住在完成后将其删除!),只需将flatten :: (a, (b, c)) -> (a, b, c)
flatten x = (fst x, fst(snd x), snd(snd x))
flatten2 :: ((a, b), c) -> (a, b, c)
flatten2 x = (fst(fst x), snd(fst x), snd x)
放入模块中即可。
答案 15 :(得分:1)
这是我的 this answer 版本,如果文件有(例如)语法错误,它会处理不加载文件
function reacquire(module) {
const fullpath = require.resolve(module);
const backup = require.cache[fullpath];
delete require.cache[fullpath];
try {
const newcopy = require(module);
console.log("reqcquired:",module,typeof newcopy);
return newcopy;
} catch (e) {
console.log("Can't reqcquire",module,":",e.message);
require.cache[fullpath] = backup;
return backup;
}
}
答案 16 :(得分:0)
如果是单元测试,另一个好用的工具是proxyquire。每次代理模块时,它都会使模块缓存失效并缓存一个新缓存。它还允许您修改正在测试的文件所需的模块。