[NodeJsシリーズ]NodeJsモジュール機構
注:1.本文に関わるnodejsのソースコードは、特別な説明がなければ、すべてv 10.14.1に基づく.
NodeJsシリーズに興味があれば、WeChat公式アカウントに注目してください.フロントエンドの神盾局またはgithub NodeJsシリーズの記事です.
Nodejsでモジュールの実現
このセクションは主にNodeJsのソースコードに基づいて、モジュールの実装について簡単な概観を行います.
パス解析
パス分析は実はモジュール検索の過程です.resoveFilename関数が実装されます.
一例を通して、説明を展開します.
コアモジュールの場合、ファイルモジュール カタログモジュール node_からmodulesディレクトリ負荷 グローバルディレクトリ負荷 これらは公式文書で明らかにされているので、ここでは詳しく説明しません.
モジュールが存在する場合、
モジュールをロード
モジュールアドレスを取得すると、Nodeはモジュールのロードに着手します.
まず、Nodeはモジュールがキャッシュ中にあるかどうかを確認します..jsファイル ファイルの内容をfs同期で読み取って指定関数に包む:.jsonファイル ファイルの内容をfs同期で読み込み、.node これはC/C++で作成した拡張ファイルで、最後にコンパイルしたファイルをdleopen()でロードします..mjs これはES 6モジュールの拡張ファイルを処理するためのもので、NodeJsがv 8.5.0後に追加した特性です.このような拡張子のファイルは、ES 6モジュール文法
次にモジュールの循環依存性の問題を探ってみましょう.モジュール1依存モジュール2、モジュール2依存モジュール1は、何が発生しますか?
ここでは、comonjsの状況だけを探究します.
このために、私達は2つのファイルを作成しました.module-a.jsとmodule-b.js、彼らに相互参照させます.
module-a.js
QAモジュールキャッシュはどうやって削除しますか? 対応するモジュールのキャッシュは、 NodeでES 6モジュールが使えますか? 8.5.0バージョンから、NodeJsはオリジナルES 6モジュールをサポートし始めました.この機能を有効にするには2つの条件が必要です. ES 6モジュールを使用するすべてのファイル拡張子は、mjs でなければなりません.コマンドラインオプション--experimental-modules node--experimental-modules index.mjs
参照 nodejs-loader.js 朴霊.深入浅出Node.js
NodeJsシリーズに興味があれば、WeChat公式アカウントに注目してください.フロントエンドの神盾局またはgithub NodeJsシリーズの記事です.
Nodejsでモジュールの実現
このセクションは主にNodeJsのソースコードに基づいて、モジュールの実装について簡単な概観を行います.
require
を使用してモジュールを導入すると、概観的には、2つのステップ:経路分析とモジュールロードパス解析
パス分析は実はモジュール検索の過程です.resoveFilename関数が実装されます.
一例を通して、説明を展開します.
const http = require('http');
const moduleA = requie('./parent/moduleA');
この例では、コアモジュールhttp
とカスタムモジュールmoduleA
との2つの異なるタイプのモジュールを導入する.コアモジュールの場合、
_resolveFilename
は、検索ステップをスキップして、直接に戻り、次の処理に移る.if (NativeModule.nonInternalExists(request)) {
// request 'http'
return request;
}
カスタムモジュールには、以下のようなケースがあります.モジュールが存在する場合、
_resolveFilename
は、/Users/xxx/Desktop/practice/node/module/parent/moduleA.js
のようなモジュールの絶対パスを返す.モジュールをロード
モジュールアドレスを取得すると、Nodeはモジュールのロードに着手します.
まず、Nodeはモジュールがキャッシュ中にあるかどうかを確認します.
// filename
var cachedModule = Module._cache[filename];
if (cachedModule) {
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
存在すれば対応キャッシュ内容に戻り、存在しない場合はさらにこのモジュールがコアモジュールかどうかを判断する.if (NativeModule.nonInternalExists(filename)) {
return NativeModule.require(filename);
}
モジュールがキャッシュ中にもコアモジュールでもない場合、Nodeは新たなモジュールオブジェクトを実装します.
function Module(id, parent){
//
this.id = id;
//
this.exports = {};
//
this.parent = parent;
this.filename = null;
//
this.loaded = false;
//
this.children = [];
}
var module = new Module(filename, parent);
そして、Nodeは経路によってロードを試みる.function tryModuleLoad(module, filename) {
var threw = true;
try {
module.load(filename);
threw = false;
} finally {
if (threw) {
delete Module._cache[filename];
}
}
}
異なるファイルの拡張子については、ロード方法が異なります.Module.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'
});'
];
呼び出しはこの関数を実行します.compiledWrapper.call(this.exports, this.exports, require, this,
filename, dirname);
JSON.parse
で解析して内容に戻ります.var content = fs.readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
return process.dlopen(module, path.toNamespacedPath(filename));
import
を使用してのみ導入できます.そうでないとエラーが発生します.throw new ERR_REQUIRE_ESM(filename);
すべてがうまくいけば、exportsの対象に付加された内容に戻ります.return module.exports;
モジュール循環依存性次にモジュールの循環依存性の問題を探ってみましょう.モジュール1依存モジュール2、モジュール2依存モジュール1は、何が発生しますか?
ここでは、comonjsの状況だけを探究します.
このために、私達は2つのファイルを作成しました.module-a.jsとmodule-b.js、彼らに相互参照させます.
module-a.js
console.log(' A ');
exports.a = 2;
require('./module-b.js');
exports.b = 3;
console.log('A ');
module-b.jsconsole.log(' B ');
let moduleA = require('./module-a.js');
console.log(moduleA.a,moduleA.b)
console.log('B ');
--experimental-modules
を実行すると、コンソール出力が見られます. A
B
2 undefined
B
A
このとき、各module-a.js
は同期して実行されるので、require
が完全にローディングされる前にmodule-a
をローディングする必要があり、./module-b
にとって、module-a
オブジェクトには属性exports
だけが付加されており、属性a
はb
のローディングが完了した後に値が付与される.QA
./module-b
によって削除されてもよく、moduleIdはモジュールの絶対パスを表しています.一般的に、いくつかのモジュールを熱的に更新する必要がある場合、この特性を使用して、例を挙げます.// hot-reload.js
console.log('this is hot reload module');
// index.js
const path = require('path');
const fs = require('fs');
const hotReloadId = path.join(__dirname,'./hot-reload.js');
const watcher = fs.watch(hotReloadId);
watcher.on('change',(eventType,filename)=>{
if(eventType === 'change'){
delete require.cache[hotReloadId];
require(hotReloadId);
}
});
node --experimental-modules index.mjs
しかし、NodeJs v 10.15.0まで、ES 6モジュールのサポートはまだ実験的です.筆者は会社のプロジェクトで使うことを勧めません.参照