node.js require()キャッシュ-無効にすることは可能ですか?


325

node.jsのドキュメントから:

モジュールは、最初にロードされた後にキャッシュされます。これは、(とりわけ)require( 'foo')を呼び出すたびに、同じファイルに解決される場合、まったく同じオブジェクトが返されることを意味します。

このキャッシュを無効にする方法はありますか?つまり、単体テストでは、各テストが新しいオブジェクトで機能するようにします。


8
NPMモジュールgithub.com/gajus/require-new
— Gajus

ウォッチャーを備えた別のNPMモジュール:npmjs.com/package/updated-require
— Jorge

これは、異なるスコープのためにそれを必要とせずに使用したファイルの内容をキャッシュし、evalに可能ですstackoverflow.com/questions/42376161/...
— lonewarrior556

回答:


305

循環依存がある場合でも、問題なく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();

2
初めて{ ... a: undefined}必要なb.js場合は、その理由を教えてください。等しいと期待し'a from a.js'ます。ありがとう
— ira 2017

1
なぜ未定義なのですか?
— Jeff P Chacko 2017

4
遅い回答ですがb[a]、循環依存関係があるため、初めて収集したものからは未定義です。a.js必要とb.js順番に必要としますa.js。a.jsはまだ完全にロードされておらず、まだexports.a定義されていないため、b.js何も取得しません。
— nik10110 2017

require.main.require(path)ここで説明されているように使用している場合、これを行う方法はありますか?stackoverflow.com/questions/10860244/...
— Flion

186

常にモジュールをリロードしたい場合は、次の関数を追加できます。

function requireUncached(module) {
    delete require.cache[require.resolve(module)];
    return require(module);
}

そして、requireUncached('./myModule')requireの代わりに使用します。


6
これはfs.watch、ファイルの変更をリッスンするメソッドと組み合わせると完璧です。
— ph3nx 14

2
リスクは何ですか?
— Scarass

私が持っているのと同じ質問ですが、このソリューションを使用するリスクは何ですか?
— rotimi-best

1
本当に同じです。コードの構造によっては、コードを再度初期化しようとするとクラッシュする可能性があります。例 モジュールがサーバーを起動し、ポートを待機する場合。次にモジュールをrequireUncachedするとき、そのポートはすでに開いているため、失敗します。
— ラフ、

133

はい、アクセスしたいモジュールの名前が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は、ユーザーが愚かなことをするのを止めるようには設計されていません。–ダググウィン

キャッシュされていないモジュールを明示的にロードする方法があったはずだと思います。


17
ダグの引用のためだけに+1。私も誰かが私が信じていることを
— 口にする

1
正解です。リロードを有効にしてノード複製を開始したい場合は、この要点を確認してください。
— gleitz 2013年

1
驚くばかり。これを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
— ベンタエル2014

くだらない。私のコメントはひどいです。このコメントにコードをきちんと追加できず、編集するには遅すぎるので、私は答えました。@Ben Barkay質問を編集してコードの小さなスニペットを追加できる場合require.uncache
— ベンタエル14

@bentaelに感謝します。これを私の回答に追加しました。
— Ben Barkay 2014

39

そのためのシンプルなモジュールがあります(テスト付き)

コードのテスト中にこの正確な問題がありました(キャッシュされたモジュールを削除して、新しい状態で再要求できるようにするため)。さまざまなStackOverflowのQ&Aに関する人々の提案をすべて見直し、単純な node.jsモジュール(テスト付き):

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

ご想像のとおり、公開されたnpmパッケージとローカルで定義されたモジュールの両方で機能します。Windows、Mac、Linuxなど

ビルドステータス codecov.io コード気候の保守性 依存関係ステータス devDependenciesステータス

どうやって?(使い方)

使い方はとても簡単です:

インストール

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


1
私はこれを見ていて、特定の条件下でモジュールをアンロードおよび再ロードできるようにテストするときに使用するのは本当に素晴らしいように見えますが、残念ながら私は働いており、私の会社はGPLライセンスから遠ざかっています。私はそれをテストのためだけに使用したいので、それはとても便利に見えるので私はまだそれを検討しています。
— Matt_JD 2016年

@Matt_JDフィードバックありがとうございます。どちらのライセンスがお好みですか?
— nelsonic 2016年

2
@Matt_JD MITのライセンスを更新しました。頑張ってね!:-)
— nelsonic 2016年

1
これは驚くほどうまくいきました!このレポにスターを付け、この回答に賛成票を投じます。
— aholt 2018年

1
今日の最新のv14.2.0で問題なく実行することを強くお勧めします
— Thomazella

28

Jestを使用している人がこれに遭遇した場合は、Jestが独自のモジュールキャッシングを行うため、このための組み込み関数があります。 jest.resetModulesます。各テストの後:

afterEach( function() {
  jest.resetModules();
});

デキャッシュを使用しようとした後にこれを見つけました別の答えが示唆されているように。アンソニーガーバンに感謝します。

関数のドキュメントはこちら。


1
このメモをどうもありがとう!
— mjgpy3

2
これを発見するまでにどれくらいの期間実験をしたのでしょうか。
— ティアゴ

16

解決策は以下を使用することです:

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] }

それdelete require.cache[require.resolve()]がラフの答えに示されているものを使用できるときです:

> delete require.cache[require.resolve('./example.js')]
true
> require('./example.js')
{ message: 'hi', say: [Function], farewell: 'bye!' }

したがって、キャッシュが消去されrequire()、がファイルのコンテンツを再度キャプチャして、現在の値をすべてロードします。


私見これは最も適切な答えです
— Piyush Katariya

5

rewireはこの使用例に最適です。呼び出しごとに新しいインスタンスを取得します。node.jsユニットテスト用の簡単な依存性注入。

rewireは特別なセッターとゲッターをモジュールに追加するので、ユニットテストを改善するためにそれらの動作を変更できます。してもいいです

他のモジュールまたはプロセスリークのようなグローバルのモックを注入するプライベート変数は、モジュール内の変数をオーバーライドします。rewireはファイルをロードせず、ノードの必要なメカニズムをエミュレートするためにコンテンツを評価しません。実際、それはモジュールをロードするためにノード自身の要求を使用します。したがって、モジュールはテスト環境でも通常の状況とまったく同じように動作します(変更を除く)。

すべてのカフェイン中毒者にとって朗報です。rewireはCoffee-Scriptでも機能します。この場合、CoffeeScriptをdevDependenciesにリストする必要があることに注意してください。


4

luffの答えに1行追加して、パラメーター名を変更します。

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

はい、キャッシュを無効にできます。

( -例えばキャッシュがあなたがファイル名に応じて直接アクセスすることができます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


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


3

「無効化」の意味は100%確実ではありませんが、requireステートメントの上に以下を追加してキャッシュをクリアできます。

Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })

Dancrumbさんのコメント@から撮影ここ


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');

1

次の2つのステップの手順は、私にとって完全に機能しています。

Modelファイルを'mymodule.js'動的に変更した後、mongooseモデルのプリコンパイル済みモデルを削除してから、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');

0

単体テストの場合は、proxyquireを使用することもできます。モジュールをプロキシ要求するたびに、モジュールキャッシュが無効になり、新しいキャッシュがキャッシュされます。また、テストするファイルに必要なモジュールを変更することもできます。


0

ロード後にキャッシュからモジュールを削除する小さなモジュールを作成しました。これにより、次に必要になったときにモジュールの再評価が強制されます。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を使用するより多くのトリックが必要です

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.