我爱编程

[译] node.js require()的缓存-可以使其失效吗

2017-04-11  本文已影响2522人  d6e83ee69161

原文:node.js require() cache - possible to invalidate?

问:

下面这段来自于node.js文档:

模块在第一次载入后就被缓存起来了。这意味着(在其它地方)每一次调用require('foo')将会返回同样的一个对象,如果它被解析为同一个文件。

有没有方法让这个缓存失效?这是为了单元测试,我想要让每一个测试检测的是全新的对象。

答1:

你总可以安全删去require.cache中的某个入口,这没有任何问题,即使里面存在着循环依赖。因为当你删除的时候,你只是删除了这项该缓存模块对象的引用,而不是模块对象本身,该模块对象将不会被垃圾回收以防循环引用,仍然会有一个对象引用指向该模块对象。假设你有一个脚本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' }

答2:

当然可以,你可以通过require.cache[moduleName]的方式来操作cache,前提是moduleName是你想要访问的模块的名字。通过delete require.cache[moduleName]命令来删除某个入口,将会导致require重新去加载真正的文件。

下面是一段代码,能移除所有跟该模块相关的文件的缓存:

/**
 * 从缓存中移除module
 */
function purgeCache(moduleName) {
    // 遍历缓存来找到通过指定模块名载入的文件
    searchCache(moduleName, function (mod) {
        delete require.cache[mod.id];
    });

    // 删除模块缓存的路径
    // 多谢@bentael指出这点
  Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
        if (cacheKey.indexOf(moduleName)>0) {
            delete module.constructor._pathCache[cacheKey];
        }
    });
};

/**
 * 遍历缓存来查找通过特定模块名缓存下的模块
 */
function searchCache(moduleName, callback) {
    //  通过指定的名字resolve模块
    var mod = require.resolve(moduleName);

    // 检查该模块在缓存中是否被resolved并且被发现
    if (mod && ((mod = require.cache[mod]) !== undefined)) {
        // 递归的检查结果
        (function traverse(mod) {
            // 检查该模块的子模块并遍历它们
            mod.children.forEach(function (child) {
                traverse(child);
            });

            // 调用指定的callback方法,并将缓存的module当做参数传入
            callback(mod);
        }(mod));
    }
};

使用方法如下:

// 载入包
var mypackage = require('./mypackage');

// 从cache中清除该包
purgeCache('./mypackage');

因为这段代码使用了和require同样的resolver,所以你可以指定任意你想要require的模块。

"Unix was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things." – Doug Gwyn

我认为应该有一种方法来显式的加载非缓存模块。

上一篇下一篇

猜你喜欢

热点阅读