从 node.js 文档中:
模块在第一次加载后被缓存。这意味着(除其他外)每次调用 require('foo') 都将返回完全相同的对象,如果它会解析为同一个文件。
有没有办法使这个缓存失效?即对于单元测试,我希望每个测试都在一个新的对象上工作。
即使存在循环依赖项,您也始终可以安全地删除 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' }
===
如果直接运行 node.js,以上是有效的。但是,如果使用具有自己的模块缓存系统的工具,例如 jest,正确的语句应该是:
jest.resetModules();
如果你总是想重新加载你的模块,你可以添加这个函数:
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 的设计不是为了阻止它的用户做愚蠢的事情,因为这也会阻止他们做聪明的事情。” – 道格·格温
我认为应该有一种方法可以执行显式的未缓存模块加载。
require.uncache
函数中。 ``` // 参见 github.com/joyent/node/issues/8266 Object.keys(module.constructor._pathCache).forEach(function(k) { if (k.indexOf(moduleName)>0) delete module.constructor._pathCache[k]; } ); ``` 假设你需要一个模块,然后卸载它,然后重新安装相同的模块,但使用了不同的版本,在它的 package.json 中有不同的主脚本,下一个 require 将失败,因为那个主脚本不存在,因为它缓存在 Module._pathCache
require.uncache
有一个简单的模块(带有测试)
我们在测试我们的代码时遇到了这个确切的问题(删除缓存的模块,以便它们可以在新状态下重新需要),所以我们审查了人们在各种 StackOverflow 问题和答案中的所有建议,并组合了一个简单的 node.js 模块(带测试):
https://www.npmjs.com/package/decache
如您所料,适用于已发布的 npm 包和本地定义的模块。 Windows、Mac、Linux 等
https://img.shields.io/travis/dwyl/decache/master.svg?style=flat-square
如何? (用法)
用法很简单:
安装
从 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
对于遇到此问题的任何使用 Jest 的人,因为 Jest 进行自己的模块缓存,因此有一个内置函数 - 只需确保 jest.resetModules
运行,例如。每次测试后:
afterEach( function() {
jest.resetModules();
});
在尝试使用 decache 后发现这个,就像另一个答案所建议的那样。感谢Anthony Garvan。
函数文档 here。
解决方案是使用:
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()
再次捕获文件的内容,加载所有当前值。
rewire 非常适合此用例,每次调用都会获得一个新实例。 node.js 单元测试的简单依赖注入。
rewire 为模块添加了一个特殊的 setter 和 getter,因此您可以修改它们的行为以进行更好的单元测试。您可以
为其他模块或全局注入模拟,例如进程泄漏私有变量覆盖模块内的变量。 rewire 不会加载文件并评估内容以模拟节点的 require 机制。事实上,它使用节点自己的 require 来加载模块。因此,您的模块在测试环境中的行为与常规情况下完全相同(除了您的修改)。
所有咖啡因成瘾者的好消息:rewire 也适用于 Coffee-Script。请注意,在这种情况下,CoffeeScript 需要在您的 devDependencies 中列出。
我会在 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)
}
是的,您可以使缓存无效。
缓存存储在一个名为 require.cache 的对象中,您可以根据文件名直接访问该对象(例如 - /projects/app/home/index.js
,而不是您在 require('./home')
语句中使用的 ./home
)。
delete require.cache['/projects/app/home/index.js'];
我们的团队发现以下模块很有用。使某些模块组无效。
https://www.npmjs.com/package/node-resource
我不是 100% 确定您所说的“无效”是什么意思,但您可以在 require
语句上方添加以下内容以清除缓存:
Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })
摘自 @Dancrumb 的评论 here
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');
我无法在答案的评论中整齐地添加代码。但我会使用@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 中有不同主脚本的不同版本,下一个 require 将失败,因为该主脚本不存在,因为它缓存在Module._pathCache
如果您希望模块永远不会被缓存(有时对开发很有用,但请记住在完成后将其删除!)您可以将 delete require.cache[module.id];
放入模块中。
以下两步程序对我来说非常有效。
动态更改 Model
文件即 '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');
这是我的 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;
}
}
我做了一个小模块,在加载后从缓存中删除模块。这会在下次需要时强制重新评估模块。请参阅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/ 中需要更多使用 Node 的技巧
b.js
时解释为什么{ ... a: undefined}
?我希望等于'a from a.js'
。谢谢b[a]
第一次是未定义的,因为存在循环依赖。a.js
需要b.js
,而b.js
又需要a.js
。a.js
尚未完全加载,exports.a
尚未定义,因此b.js
什么都没有。require.main.require(path)
,有什么方法可以做到这一点? stackoverflow.com/questions/10860244/…