node.jsの完全な遅延読み込み

Node.js 6.0のリリースにより、正直な遅延ブートローダーを整理するための既成のコンポーネントセットがすぐに使用できるようになりました。 この場合、私はlazyloadを意味します。これは、目的のモジュールが名前で要求され、現在のモジュールのグローバルスコープ内にある場合にのみ、目的のモジュールを見つけてロードしようとします。 Node.JSの記事に基づいて書かれたrequire()forever、ローカルモジュールとオンデマンドでのモジュールのロードをサポートするノードjsのモジュールローダーを 取り除き ます


この記事はより本質的な研究であり、その目的はNode.jsの機能を示し、ES 2015の革新の真の利点を示し、JSの既存の機能を再確認することです。 このアプローチは実稼働環境でテストされていますが、いくつかのトラップがあり、思慮深いアプリケーションが必要であることに注意してください。記事の最後で、これについて詳しく説明します。 このDIは、アプリケーションプログラムで簡単に使用できます。


作業コードを含むリポジトリへのリンクをすぐに提供します。


それで、システムの基本的な要件を説明しましょう。



次のように機能します。


// script.js speachModule.sayHello(); 

 // deps/speach-module.js exports.sayHello = function() { console.log('Hello'); }; 

疑似グローバルスコープ


疑似グローバルスコープとは何ですか? これは、任意のファイルから使用可能な変数のスコープですが、現在のモジュール内のみです。 つまり node_modulesからのモジュール、またはモジュールルートの上にあるモジュールからはアクセスできません。 しかし、これを達成する方法は? これを行うには、Node.jsモジュール読み込みシステムを調べる必要があります。


exception.jsファイルを作成します。


 throw 'test error'; 

そして、それを実行します:


 node exception.js 

トレース内のエラーの位置ラベルを見てください。明らかに、期待したものとは異なります。


実際には、Node.jsのモジュールローディングシステム自体が接続されると、そのコンテンツは関数でラップされます


 NativeModule.wrap = function(script) { return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; }; NativeModule.wrapper = [ '(function (exports, require, module, __filename, __dirname) { ', '\n});' ]; 

他の環境のように、exports、require、 dirname、 filenameは魔法の変数ではありません。 また、モジュールコードは単に関数に変わり、必要な引数を使用して実行されます。


同じ原理で動作する独自のブートローダーを作成し、それをデフォルトのものと置き換えてから、モジュール変数を管理し、必要に応じて独自のものを追加できます。 すばらしいですが、存在しない変数への呼び出しをインターセプトする必要があります。 これを行うには、 withを使用with 。これは、グローバルスコープと現在のスコープを仲介します。正しいスコープを取得するために各モジュールに対してscopeLookupメソッドを使用します。 、残りはglobal渡します。


かなり頻繁に、変数の置換に関連するエラーの非自明性ととらえどころのないことについて批判されています。 しかし、適切に使用すれば、予想以上の動作をします。

これは、ラッパーが現在どのように見えるかです:


 var wrapper = [ '(function (exports, require, module, __filename, __dirname, scopeLookup) { with (scopeLookup(__dirname)) {', '\n}});' ]; 

リポジトリ内の完全なブートローダーコードと例。


上で書いたように、スコープ自体はscope.jsファイルに保存されます。 これは、スコープの変更を行って追跡するプロセスをより明確にするために必要です。


オンデマンドモジュールの読み込み


いいね エクスポートオブジェクトに擬似グローバルスコープ値が含まれるscope.jsファイルが作成されました。 事は小さいです:エクスポートオブジェクトをプロキシインスタンスに置き換え、必要なモジュールをその場でロードするように訓練します:


 const fs = require('fs'); const path = require('path'); const decamelize = require('decamelize'); //   scope const scope = {}; module.exports = new Proxy(scope, { has(target, prop) { if (prop in target) { return true; } if (typeof prop !== 'string') { return; } var filename = decamelize(prop, '-') + '.js'; var filepath = path.resolve(__dirname, 'deps', filepath); return fs.existsSync(filepath); }, get(target, prop) { if (prop in target) { return target[prop]; } if (typeof prop !== 'string') { return; } var filename = decamelize(prop, '-') + '.js'; var filepath = path.resolve(__dirname, 'deps', filename); if (fs.existsSync(filepath)) { return scope[prop] = require(filepath); } return null; } }); 

それだけです。 その結果、Node.jsで実際の遅延ロードを取得しました。これは、他のモジュールからは見えず、ファイルヘッダーの巨大なrequire-blocksを回避し、もちろん、システムの初期化を高速化できます。


明白な困難:


  1. このアプローチでは、テストカバレッジを計算するコードを生成する独自の方法を記述する必要があります。
  2. ブートローダーを接続する別のエントリポイントが必要です。

これで、テストのコード、gulp / gruntファイルなどでこのようなローダーを使用できます。



Source: https://habr.com/ru/post/J283086/


All Articles