从node.js文档中:
第一次加载模块后将对其进行缓存。这意味着(除其他事项外)每次对require('foo')的调用都将获得完全相同的返回对象,如果它可以解析为相同的文件。
有没有办法使该缓存无效?即对于单元测试,我希望每个测试都可以在一个新对象上进行。
从node.js文档中:
第一次加载模块后将对其进行缓存。这意味着(除其他事项外)每次对require('foo')的调用都将获得完全相同的返回对象,如果它可以解析为相同的文件。
有没有办法使该缓存无效?即对于单元测试,我希望每个测试都可以在一个新对象上进行。
Answers:
即使存在循环依赖关系,也始终可以安全地删除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();
{ ... a: undefined}需要b.js吗?我期望平等'a from a.js'。谢谢
b[a]由于存在循环依赖关系,因此我第一次收集到的信息是不确定的。a.js要求b.js反过来要求a.js。a.js尚未完全加载并且exports.a尚未定义,因此b.js什么也没得到。
require.main.require(path)按照这里的描述使用它,有什么办法吗?stackoverflow.com/questions/10860244/…–
如果您始终想重新加载模块,则可以添加以下功能:
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
然后使用requireUncached('./myModule')而不是require。
fs.watch侦听文件更改的方法完美结合。
是的,您可以通过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)
我认为应该有一种方法可以执行显式的未缓存模块加载。
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
require.uncache
我们在测试代码时遇到了这个确切的问题(删除缓存的模块,以便可以在新的状态下重新请求它们),因此我们回顾了人们对各种StackOverflow问题与解答的所有建议,并整理了一个简单的 node.js模块(与测试):
如您所料,它既可以用于已发布的npm软件包,也可以用于本地定义的模块。Windows,Mac,Linux等
用法很简单:
从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
解决方案是使用:
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答案中delete require.cache[require.resolve()]指示的时间:
> delete require.cache[require.resolve('./example.js')]
true
> require('./example.js')
{ message: 'hi', say: [Function], farewell: 'bye!' }
因此,清除了缓存,并require()再次捕获了文件的内容,并加载了所有当前值。
rewire在此用例中非常有用,每次调用都会获得一个新实例。轻松的依赖注入,用于node.js单元测试。
rewire在模块中添加了特殊的setter和getter,因此您可以修改它们的行为以进行更好的单元测试。你可以
为其他模块或全局变量(例如进程泄漏专用变量)注入模拟,将覆盖模块内的变量。rewire不会加载文件并评估内容以模拟节点的require机制。实际上,它使用节点自身的要求来加载模块。因此,您的模块在测试环境中的行为与常规情况下完全相同(修改除外)。
对所有咖啡因上瘾者来说是个好消息:rewire也可以在Coffee-Script中使用。请注意,在这种情况下,需要在devDependencies中列出CoffeeScript。
我会再增加一行,并更改参数名称:
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)
}
是的,您可以使缓存无效。
缓存存储在一个名为require.cache的对象中,您可以根据文件名直接访问该对象(例如,/projects/app/home/index.js与./home在require('./home')语句中使用的文件名不同)。
delete require.cache['/projects/app/home/index.js'];
我们的团队发现以下模块很有用。使某些模块组无效。
我无法在答案的注释中整齐地添加代码。但是我会使用@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
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');
接下来的两步过程对我来说是完美的。
动态更改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');
如果用于单元测试,则另一个好的工具是proxyquire。每次您代理查询模块时,它将使模块缓存无效并缓存一个新的缓存。它还允许您修改要测试的文件所需的模块。
我做了一个小模块,以便在加载后从缓存中删除模块。这将在下次需要时重新评估模块。参见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:Node的更多技巧需要在我的https://glebbahmutov.com/blog/hacking-node-require/中进行