从node.js文档中:


第一次加载模块后对其进行缓存。这意味着(除其他事项外)每次对require('foo')的调用都将返回完全相同的对象(如果它可以解析为相同的文件)。使该缓存无效?即对于单元测试,我希望每个测试都可以在一个新对象上进行。

评论

NPM模块github.com/gajus/require-new

另一个带有观察程序的NPM模块:npmjs.com/package/updated-require

可以在不使用require的情况下缓存文件内容,并将其评估为不同的作用域stackoverflow.com/questions/42376161/…

#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/