1. はじめにNode.js は CommonJS 仕様に基づいてモジュール化されていることはご存じのとおりです。モジュール化は複雑なビジネス シナリオに欠かせないツールです。モジュール化は頻繁に使用していますが、体系的に理解したことがないかもしれません。そこで今日は、Node.js のモジュール化について知っておくべきことをいくつか説明し、Node.js のモジュール化について詳しく見ていきます。 2. 本文Node.js には、モジュール管理用に 2 つのモジュールが組み込まれています。これらの 2 つのモジュールは、require と module というよく知られた 2 つのキーワードでもあります。組み込みとは、他のモジュールのように参照することなく、これら 2 つのモジュールをグローバル スコープで使用できることを意味します。
Node.js でモジュールを参照するのは難しくありません。とても簡単です: const config = require('/path/to/file') しかし実際には、この単純なコードは合計 5 つのステップを実行します。 これら 5 つのステップを理解することで、Node.js のモジュール性の基本原則を理解し、落とし穴を特定するのにも役立ちます。これら 5 つのステップが何を行うのかを簡単にまとめてみましょう。
学生の中には、これらの 5 つのステップを理解し、これらの原則に精通している学生もいるかもしれませんが、心の中で疑問を抱いている学生もいるかもしれません。いずれにせよ、次のコンテンツでは、上記の実行手順を詳細に分析し、誰もが質問に答えたり、知識を統合してギャップを埋めたりするのに役立つことを願っています。 ちなみに、必要であれば私のように実験ディレクトリを構築し、デモに従って実験を行うこともできます。 2.1 モジュールとは何ですか?モジュール性を理解するには、まずモジュールとは何かを理解する必要があります。 Node.js では、ファイルはモジュールであることがわかっています。モジュールは .js、.json、または .node ファイルになる可能性があると先ほど説明しました。これらを参照することで、ツールの機能、変数、構成などを取得できますが、その具体的な構造は何でしょうか。モジュール、つまりモジュール オブジェクトの構造を確認するには、コマンド ラインで次のコマンドを実行するだけです。
モジュールは単なる普通のオブジェクトであることがわかりますが、構造にはいくつかの特別な属性値があり、それらを 1 つずつ理解する必要があります。id、parent、filename、children などの一部の属性は説明する必要もなく、文字通りの意味から理解できます。 以下の内容は、これらのフィールドの意味と機能を理解するのに役立ちます。 2.2 解決モジュールの概要を理解した後、最初のステップである「解決」から始め、モジュール化の原理、つまり Node.js がターゲット モジュールを見つけて、ターゲット モジュールの絶対パスを生成する方法を理解します。 では、なぜ今、モジュールの構造を誰もが理解できるように、モジュール オブジェクトを印刷したのでしょうか? id、パス、解決手順という2つのフィールド値があるため、これらは密接に関連しています。一緒に見てみましょう。 まず、id属性: 各モジュールには id 属性があり、通常はモジュールのフルパスです。Node.js はこの値を使用してモジュールを識別し、見つけることができます。ただし、ここでは特定のモジュールはなく、コマンドラインでモジュール構造を出力するだけなので、デフォルトの <repl> 値になります (repl は対話型インタープリターを意味します)。 次は paths 属性です。 この paths 属性の機能は何ですか? Node.js では、相対パス、絶対パス、プリセット パス (後ほど説明します) など、さまざまな方法でモジュールを参照できます。find-me というモジュールを参照する必要がある場合、require はどのようにしてこのモジュールを見つけるのに役立つのでしょうか。 要求('find-me') パスにあるものを出力してみましょう: ~/learn-node $ ノード > モジュールパス [ '/Users/samer/learn-node/repl/node_modules', '/Users/samer/learn-node/node_modules', '/Users/samer/node_modules', '/Users/node_modules', '/node_modules', '/Users/samer/.node_modules', '/Users/samer/.node_libraries', '/usr/local/Cellar/node/7.7.1/lib/node'] はい、これは実際には一連のシステム絶対パスです。これらのパスは、すべてのターゲット モジュールの可能な場所を表し、順序付けられています。つまり、Node.js は、パスにリストされているすべてのパスを順番に検索します。モジュールが見つかった場合は、後で使用するためにモジュールの絶対パスを出力します。 これで、Node.js がこのディレクトリの山からモジュールを検索することがわかりました。find-me モジュールを見つけるために require('find-me') を実行しようとします。find-me モジュールをどのディレクトリにも配置していないため、Node.js はすべてのディレクトリを走査してもターゲット モジュールを見つけることができず、モジュール 'find-me' が見つかりませんというエラーを報告します。このエラーはよく見かけることがあります。
ここで、参照する必要がある find-me モジュールを上記のいずれかのディレクトリに配置します。ここでは、node_modules ディレクトリを作成し、Node.js が見つけられるように find-me.js ファイルを作成します。
find-me.js ファイルを手動で作成した後、Node.js はターゲット モジュールを見つけました。もちろん、find-me モジュールが Node.js のローカル node_modules ディレクトリで見つかった場合、後続のディレクトリでは検索を続行しません。 Node.js 開発経験のある学生は、モジュールを参照するときに正確なファイルを指定する必要がないことに気付くでしょう。ディレクトリを参照してターゲット モジュールを参照することもできます。例:
find-me ディレクトリ内の index.js ファイルが自動的にインポートされます。 もちろん、これはルールの制約を受けます。Node.js が find-me ディレクトリで index.js ファイルを見つけることができる理由は、デフォルトのモジュール インポート ルールでは、特定のファイル名が見つからない場合に index.js ファイルを探すようになっているためです。また、package.json を変更して、index -> main などのインポート ルールを変更することもできます。
2.3、require.resolveモジュールをすぐに実行せずにプロジェクトに導入したいだけの場合は、 require.resolve メソッドを使用できます。このメソッドは require メソッドに似ていますが、導入されたモジュール メソッドは実行されません。
ご覧のとおり、モジュールが見つかった場合、Node.js はモジュールの完全なパスを出力し、見つからない場合はエラーを報告します。 Node.js がモジュールを探す方法がわかったので、Node.js がモジュールをロードする方法を見てみましょう。 2.4. モジュール間の親子依存関係モジュール間の参照関係を親子依存関係として表現します。 lib/util.js ファイルを作成し、これが参照されているサブモジュールであることを示す console.log ステートメントを追加するだけです。
また、index.js に console.log ステートメントの行を入力して、これを親モジュールとして識別し、サブモジュールとして作成した lib/util.js を参照します。
index.js を実行して、それらの間の依存関係を確認します。
ここでは、依存関係に関連する 2 つのプロパティ、子と親に焦点を当てます。 印刷された結果では、 children フィールドにインポートされた util.js モジュールが含まれており、これは util.js が index.js が依存するサブモジュールであることを示しています。 しかし、util.js モジュールの parent プロパティをよく見ると、ここに Circular という値が表示されていることがわかります。これは、モジュール情報を出力すると循環依存関係が生成されるためです。親モジュール情報は子モジュール情報に出力され、子モジュール情報は親モジュール情報に出力されます。そのため、Node.js は単にこれを Circular としてマークします。 親子の依存関係を理解する必要があるのはなぜですか?これは Node.js が循環依存関係を処理する方法に関連しているため、後で詳しく説明します。 循環依存関係の処理の問題を検討する前に、エクスポートと module.exports という 2 つの重要な概念を理解する必要があります。 2.5. エクスポート、モジュール.エクスポート輸出: Exports は、宣言なしで Node.js のグローバル変数として直接使用できる特別なオブジェクトです。これは実際には module.exports への参照です。exports を変更することで、module.exports を変更する目的を達成できます。 エクスポートは、先ほど出力されたモジュール構造内のプロパティ値でもありますが、ファイル内で操作しなかったため、先ほど出力された値はすべて空のオブジェクトです。これで、単純に値を割り当ててみます。 // lib/util.js の先頭に新しい行 exports.id = 'lib/util' を追加します。 // index.js の先頭に新しい行 exports.id = 'index' を追加します。 index.jsを実行します。
先ほど追加した 2 つの id 属性が exports オブジェクトに正常に追加されたことがわかります。通常のオブジェクトを操作するのと同じように、id 以外の属性を追加することもできます。もちろん、エクスポートを関数に変換することもできます。例: エクスポート = 関数() {} モジュールエクスポート: module.exports オブジェクトは、実際には require を通じて最終的に取得されるものです。モジュールを作成するときに module.exports に割り当てる値は、他のユーザーがモジュールを参照したときに取得される値になります。たとえば、lib/util での前の操作と組み合わせると次のようになります。 const util = require('./lib/util'); console.log('UTIL:', util); // 出力結果 UTIL: { id: 'lib/util' } exports オブジェクトを通じて module.exports に {id: 'lib/util'} を割り当てたので、require の結果もそれに応じて変わります。 これで、エクスポートと module.exports が何であるかの概要は理解できましたが、注意すべき小さな詳細があります。それは、Node.js でのモジュールの読み込みは同期プロセスであるということです。 モジュール構造の loaded 属性をもう一度見てみましょう。この属性は、モジュールがロードされたかどうかを示します。この属性を使用すると、Node.js モジュールのロードの同期を簡単に確認できます。 モジュールがロードされると、ロードされた値は true になります。しかし、これまでのところ、モジュールを印刷するたびに、そのステータスは false です。これは、Node.js ではモジュールの読み込みが同期しているためです。読み込みアクション (読み込みアクションにはモジュールのマーク付け、loaded 属性のマーク付けが含まれます) が完了していない場合、印刷結果はデフォルトの loaded: false になります。 この情報を確認するために、setImmediate を使用します。 // index.js内 setImmediate(() => { console.log('index.js モジュール オブジェクトが読み込まれました!', module) }); クリックしてドラッグして移動します。index.js モジュール オブジェクトが読み込まれました。モジュール { id: '.', エクスポート: [関数], 親: null、 ファイル名: '/Users/samer/learn-node/index.js', ロード済み: true、 子供たち: [モジュール{ id: '/Users/samer/learn-node/lib/util.js', エクスポート: [オブジェクト], 親: [循環], ファイル名: '/Users/samer/learn-node/lib/util.js', ロード済み: true、 子供たち: []、 パス: [オブジェクト] } ], パス: [ '/Users/samer/learn-node/node_modules', '/Users/samer/node_modules', '/Users/node_modules', '/node_modules' ] } 読み込みが完了(マーク)した後に console.log が配置されるため、読み込みステータスは loaded: true になります。これにより、Node.js モジュールの読み込みが同期プロセスであることが完全に検証されます。 エクスポート、module.exports、モジュール読み込みの同期について理解できたので、Node.js がモジュールの循環依存関係をどのように処理するかを見てみましょう。 2.6. モジュールの循環依存上記の内容では、モジュール間に親子依存関係があることを学びました。モジュール間に循環依存関係が発生した場合、Node.js はどのような対応をするのでしょうか。 module1.js と module2.js という 2 つのモジュールがあり、次のように相互に参照しているとします。 // lib/module1.js エクスポート.a = 1; require('./module2'); // ここで参照 exports.b = 2; エクスポート.c = 3; // lib/module2.js モジュール1をconstで指定します。 console.log('Module1 はここで部分的にロードされています', Module1); // module1 を参照して出力します module1.js を実行すると、出力が表示されます。
結果には {a: 1} のみが出力され、{b: 2, c: 3} は出力されません。 module1.js をよく見ると、exports.b = 2 と exports.c = 3 が実行される前に、module1.js の途中に module2.js への参照が追加されていることがわかります。この場所を循環依存が発生する場所と呼ぶと、循環依存が発生する前にエクスポートされたプロパティが結果として得られます。これは、Node.js でのモジュールの読み込みは同期プロセスであるという結論にも基づいており、これは上で検証しました。 Node.js は循環依存関係を簡単な方法で処理します。モジュールをロードするプロセスで、エクスポート オブジェクトが徐々に構築され、エクスポートに値が割り当てられます。モジュールが完全にロードされる前にインポートすると、エクスポート オブジェクトのプロパティの一部のみが取得されます。 2.7. .json と .nodeNode.js では、JavaScript ファイルを参照するだけでなく、JSON または C++ プラグイン (.json ファイルと .node ファイル) を参照するためにも require を使用できます。対応するファイルサフィックスを明示的に宣言する必要もありません。 コマンドラインで require によってサポートされているファイルの種類を確認することもできます。
require を使用してモジュールを参照する場合、Node.js は最初に .js ファイルがあるかどうかを確認します。見つからない場合は、次に .json ファイルと照合します。それでも見つからない場合は、最後に .node ファイルと照合しようとします。ただし、一般的には、混乱や参照の意図の不明確さを避けるために、.json ファイルまたは .node ファイルを参照するときにはサフィックスを明示的に指定し、.js ファイルを参照するときにはサフィックスを省略するというルールに従うことができます (オプション、またはその両方)。 .json ファイル: .json ファイルを参照することは非常に一般的です。たとえば、一部のプロジェクトでは、静的構成を .json ファイルに保存することで管理しやすくなります。次に例を示します。 { "ホスト": "ローカルホスト", 「ポート」: 8080 } 参照したり使用したりするのは簡単です: const { ホスト、ポート } = require('./config'); console.log(`サーバーはhttp://${host}:${port}で実行されます`) 出力は次のようになります。
.node ファイル: .node ファイルは C++ ファイルから変換されます。公式 Web サイトでは、C++ で実装されたシンプルな hello プラグインが提供されており、hello() メソッドを公開して文字列 world を出力します。必要に応じて、リンクにジャンプして詳細を確認し、実験を行うことができます。 node-gyp を使用して、.cc ファイルをコンパイルし、.node ファイルにビルドすることができます。プロセスも非常に簡単です。binding.gyp ファイルを構成するだけです。ここでは詳細には触れませんが、.node ファイルを生成した後は、通常どおりファイルを参照して、その中のメソッドを使用できることを知っておく必要があります。 たとえば、hello() を addon.node ファイルに変換した後、参照して使用します。 定数 addon = require('./addon'); コンソールにログ出力します。 2.8 ラッピング実際、上記の内容では、Node.js でモジュールを参照する最初の 2 つの手順である「解決」と「読み込み」について説明しました。これらはそれぞれ、モジュール パスと読み込みの問題を解決します。次に、Wrapping の機能について見てみましょう。 ラッピングとはパッケージ化を意味し、パッケージ化の対象はモジュール内に記述したすべてのコードです。つまり、モジュールを参照する場合、実際には「透明な」パッケージングのレイヤーを通過することになります。 このパッケージ化プロセスを理解するには、まず exports と module.exports の違いを理解する必要があります。 exports は module.exports への参照です。モジュール内で exports を使用してプロパティをエクスポートすることはできますが、直接置き換えることはできません。例えば: exports.id = 42; // ok、これでエクスポートは module.exports にポイントします。これは module.exports を変更することと同じです。 exports = { id: 42 }; // 役に立たない。単に { id: 42 } オブジェクトを指すだけで、module.exports に実際の変更はありません。 module.exports = { id: 42 }; // OK、module.exports を直接操作します。 エクスポート オブジェクトはすべてのモジュールのグローバル オブジェクトのように見えますが、エクスポートされたオブジェクトがどのモジュールからのものであるかを区別できるのはなぜかと疑問に思うかもしれません。これはどのように行われるのでしょうか。 ラッピングのプロセスを理解する前に、小さな例を見てみましょう。 // a.js 内 var 値 = 'global' // b.js 内 console.log(value) // 出力: グローバル // c.js 内 console.log(value) // 出力: グローバル // index.html内 ... <script src="a.js"></script> <script src="b.js"></script> <script src="c.js"></script> a.js スクリプトで値を定義すると、この値はグローバルに表示され、その後に導入される b.js と c.js がその値にアクセスできるようになります。しかし、Node.js モジュールではそうではありません。1 つのモジュールで定義された変数はプライベート スコープを持ち、他のモジュールから直接アクセスすることはできません。このプライベートスコープはどのようにして発生するのでしょうか? 答えは簡単です。モジュールをコンパイルする前に、Node.js はモジュールの内容を関数にラップし、関数スコープを通じてプライベート スコープを実装します。 require('module').wrapper を使用すると、ラッパーのプロパティを出力できます。
Node.js はファイル内のコードを直接実行しませんが、このラップされた関数を通じてコードを実行します。これにより、各モジュールにプライベート スコープが与えられ、互いに影響を及ぼしません。 このラッパー関数には、exports、require、module、__filename、__dirname の 5 つのパラメーターがあります。これらのパラメータには、arguments パラメータを通じて直接アクセスして印刷できます。
これらのパラメータについて簡単に見てみましょう。最初のパラメータ exports は最初は空 (未割り当て) です。2 番目と 3 番目のパラメータ require と module は参照したモジュールに関連するインスタンスです。これらはグローバルではありません。 4 番目と 5 番目のパラメータ __filename と __dirname は、それぞれファイル パスとディレクトリを表します。 ラップされた関数全体は、ほぼ同じことを行います。 関数 (require、モジュール、__filename、__dirname) { exports を module.exports とします。 // あなたのコード... module.exports を返します。 } 簡単に言うと、ラッピングとは、モジュール スコープをプライベート化し、module.exports を戻り値として変数またはメソッドを公開して使用できるようにすることです。 2.9 キャッシュキャッシュは簡単に理解できます。例を使って見てみましょう。
ご覧のとおり、同じモジュールが 2 回参照されていますが、情報は 1 回しか印刷されません。これは、2 回目の参照にはキャッシュが使用され、モジュールを再ロードする必要がないためです。 現在のキャッシュ情報を確認するには、require.cache を印刷します。
参照した index.js ファイルがキャッシュ内にあるため、モジュールは再ロードされないことがわかります。もちろん、リロードの目的を達成するために require.cache を削除してキャッシュの内容をクリアすることもできますが、ここでは説明しません。 結論この記事では、Node.js モジュール化を使用する際に知っておく必要のある基本原則と常識について概説し、Node.js モジュール化について誰もがより明確に理解できるようにしたいと考えています。しかし、ラッパー関数内の処理ロジックや、CommonJS の同期読み込みの問題、ES モジュールとの違いなど、より深い詳細についてはこの記事では説明しません。この記事以外で、言及されていないコンテンツをさらに詳しく調べることができます。 以上がNodeJSのモジュール化についての詳しい説明です。NodeJSのモジュール化についてさらに詳しく知りたい方は、123WORDPRESS.COMの関連記事もぜひご覧ください! 以下もご興味があるかもしれません:
|
<<: SQL 文を使用してデータを収集する場合の sum 関数と count 関数の if 判定条件の使用法の説明
>>: Nginx で Angular プロジェクトを展開する際の落とし穴
Ubuntu は比較的人気のある Linux デスクトップ システムです。最近、Ubuntu 20....
目次1. はじめに2. 選択2.1 単一列のクエリ2.2 複数の列のクエリ2.3 すべての列をクエリ...
1. はじめにこの記事では、Docker を使用して Redis を探索する方法を説明します。 Do...
データ型が datetime であるフィールド add_time を持つテーブル product が...
基本的なネットワーク構成Docker はイメージに基づいて複数のコンテナを「開く」ことができ、各コン...
HTML チェックボックスとラジオボタンスタイルの美化の簡単な例チェックボックス: XML/HTML...
最近、MySQL を始めとしてデータベースの知識を勉強し始めました。以下では、皆さんの参考になるよう...
目次前の単語同期と非同期前菜プレートを追加マクロタスク マイクロタスク約束しましょうタイマーを追加す...
1. サーバーのセットアップリモート リポジトリは実際にはローカル リポジトリと何ら変わりなく、純粋...
目次序文ウェブHTTP サーバーファイルサーバー練習する序文Node.js 開発の目的は、JavaS...
VMWare (Virtual Machine ware) は、「仮想 PC」ソフトウェア会社です。...
nginxでサポートされている仮想ホストには3つの種類があります1. ドメイン名ベースの仮想ホスティ...
目次1. axiosをインストールする2. アクシオスの使用1.ホームページでaxiosを参照する2...
1. 関連する技術的なポイントバイト版ヴュー3 ts統合ルーティングvuexを統合するAxiosを統...
実行中のコンテナに入る # コンテナに入り、新しいターミナルを開きます# docker exec -...