node.js require()cache - 可能无效?

时间:2012-02-09 12:07:16

标签: node.js

来自node.js文档:

  

模块在第一次加载后进行缓存。这意味着(除其他外)每次调用require('foo')将获得完全相同的返回对象,如果它将解析为同一个文件。

有没有办法让这个缓存无效?即对于单元测试,我希望每个测试都能处理新的物体。

17 个答案:

答案 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)

有一个简单模块 with tests

我们在测试我们的代码(删除缓存模块以便在新状态下重新需要)时遇到了这个问题,所以我们审核了所有关于各种StackOverflow问题和人们的建议回答并整理简单 node.js模块 with tests ):

  

https://www.npmjs.com/package/decache

正如您所料,两者发布了npm包和本地定义的模块。 Windows,Mac,Linux等

Build Status codecov.io Code Climate maintainability Dependencies Status devDependencies Status

如何? (使用

用法非常简单:

安装

从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'];

我们的团队发现以下模块很有用。使某些模块组无效。

https://www.npmjs.com/package/node-resource

答案 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)

The documentation说:

在需要时将模块缓存在此对象中。 通过从该对象中删除键值,下一个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。每次代理模块时,它都会使模块缓存失效并缓存一个新缓存。它还允许您修改正在测试的文件所需的模块。