ホームページ  >  記事  >  ウェブフロントエンド  >  Node.js でのモジュール パス分析について説明する記事

Node.js でのモジュール パス分析について説明する記事

青灯夜游
青灯夜游転載
2021-12-16 19:19:311939ブラウズ

この記事では、Node.js でのモジュール パス分析について説明し、Node モジュール パス分析方法を紹介します。

Node.js でのモジュール パス分析について説明する記事

##requireCase

    現在プロジェクトがあります
  • 現在のプロジェクトパス
  • /Users/rainbow/Documents/front-end/scaffoldeddevelopment/rainbow-test
  • プロジェクトのbinディレクトリ
には大量のファイルがあります。

Node.js でのモジュール パス分析について説明する記事

    /bin/index.js
  • console.log(require.resolve("."));
    // /Users/rainbow/Documents/前端/脚手架开发/rainbow-test/bin/index.js  输出bin/index.js的绝对路径
    console.log(require.resolve.paths("."));
    // [ '/Users/rainbow/Documents/前端/脚手架开发/rainbow-test/bin' ] 输出的文件可能在的路径的数组
    console.log(require.resolve("yargs"));
    // /Users/rainbow/Documents/前端/脚手架开发/rainbow-test/node_modules/yargs/index.cjs
    console.log(require.resolve.paths("yargs"));
    /*
    [
      '/Users/rainbow/Documents/前端/脚手架开发/rainbow-test/bin/node_modules',
      '/Users/rainbow/Documents/前端/脚手架开发/rainbow-test/node_modules',
      '/Users/rainbow/Documents/前端/脚手架开发/node_modules',
      '/Users/rainbow/Documents/前端/node_modules',
      '/Users/rainbow/Documents/node_modules',
      '/Users/rainbow/node_modules',
      '/Users/node_modules',
      '/node_modules',
      '/Users/rainbow/.node_modules',
      '/Users/rainbow/.node_libraries',
      '/usr/local/Cellar/node/14.3.0_1/lib/node'
    ]
    */

requireモジュール実行ファイルを解析して見つけるプロセス

1、

Nodejsプロジェクト モジュールのパス解決は、require.resolve を通じて実装されます。

    require.resolve は、
  • Module._resolveFileName メソッド
  • Module._resolveFileName を通じて実装されます。コア プロセスは次のとおりです。
      パスが組み込みモジュールであるかどうかを判断します。
    • そうでない場合は、受信パスが '/test/lerna の場合、
    • Module._resolveLookupPahts メソッドを使用して、node_modules の可能なパスを生成します。 /cli.js'、各レベルのパスの下に node_moduels のパス配列を追加します
    • Module._findPath# を通じてモジュールの実際のパスをクエリします
  • ##2,
Module._findPath

コア プロセスは次のとおりです:

クエリ キャッシュ (リクエストと
    \ を介したパスをマージすることによって生成されます) x00
  • cacheKey)
  • Module._resolveLookupPahts
  • メソッドによって生成されたパス配列を走査し、pathrequest を結合します。 ファイル パスを形成します。basePath
  • basePath
  • が存在する場合は、fs.realPahtSync を呼び出して、ファイル Cache の実際のパスを取得します。
  • Module._pathCache# へのファイルの実際のパス ## (キーはcacheKey) (Module._pathCacheはマップ)
  • 3.
  • fs.realPahtSync
Coreプロセス:

クエリ キャッシュ (キャッシュされたキーは p です。つまり、Module._findPath で生成されたパス)

    パス文字列を左から右にトラバースし、クエリ /、分割します。パスを調べ、そのパスがソフト リンクであるかどうかを判断します。ソフト リンクの場合、リンクは実際のリンクを照会し、新しいパス p を生成し、トラバースを続けます。詳細は次のとおりです:
  • 走査プロセス中に生成されたサブパス ベースは、クエリの繰り返しを避けるために knownHard とキャッシュにキャッシュされます
  • 走査が完了すると、モジュールに対応する実際のパスが取得されます。このとき、元のパスはがキーとして使用され、実際のパスが値として使用され、キャッシュに保存されます。
  • 4,
  • require.resolve.paths
は次と同等です。

Module._resolveLookupPaths. このメソッドは、node_modules の可能なすべてのパスを取得して配列を形成します。 5, require.resolve.paths

実装原則は次のとおりです:

/
    (ルート パス) の場合、# を返します。 ## 直接 ['/node_modules']
  • それ以外の場合は、パス文字列を後ろから前にたどります。/ がクエリされると、パスを分割し、最後に node_modules を追加し、パス配列に渡します。クエリ後にパス配列が見つからない/返されないまで
  • require は組み込みモジュールのメソッドを使用します

使用するときはrequire( 'yargs')

メソッドが必要な場合

実際には Module._load

method# を使用します
    # #
    Module.prototype.require = function(id) { //id = 'yargs'
      validateString(id, 'id');
      if (id === '') {
        throw new ERR_INVALID_ARG_VALUE('id', id, 'must be a non-empty string');
      }
      requireDepth++;
      try {
        return Module._load(id, this, /* isMain */ false);
      } finally {
        requireDepth--;
      }
    };
    // 参数
    id = 'yargs'
    this={
     paths: Module._nodeModulePaths(process.cwd())
    }
  • #Module._nodeModulePathsMethod

#<pre class="brush:js;toolbar:false;">// 进入mac电脑所在的逻辑: // from =&gt; /Users/rainbow/Documents/前端/脚手架开发/lerna源码/lernas //&amp;#39;from&amp;#39; is the __dirname of the module. Module._nodeModulePaths = function(from) { from = path.resolve(from); // Return early not only to avoid unnecessary work, but to *avoid* returning // an array of two items for a root: [ &amp;#39;//node_modules&amp;#39;, &amp;#39;/node_modules&amp;#39; ] if (from === &amp;#39;/&amp;#39;) return [&amp;#39;/node_modules&amp;#39;]; const paths = []; // 关键算法代码 for (let i = from.length - 1, p = 0, last = from.length; i &gt;= 0; --i) { const code = from.charCodeAt(i); if (code === CHAR_FORWARD_SLASH) { if (p !== nmLen) paths.push(from.slice(0, last) + &amp;#39;/node_modules&amp;#39;); last = i; p = 0; } else if (p !== -1) { if (nmChars[p] === code) { ++p; } else { p = -1; } } } // Append /node_modules to handle root paths. paths.push(&amp;#39;/node_modules&amp;#39;); return paths; };</pre>for ループのコア アルゴリズム分析:

Node.js でのモジュール パス分析について説明する記事

Module._loadNode.js でのモジュール パス分析について説明する記事Method

Module._load(id, this , /* isMain */ false)コア実装コードは次のとおりです:

const filename = Module._resolveFilename(request,parent,isMain);

require.resolve

Node.js プロジェクト モジュールのパス解決は、require.resolve# を通じて実装されます。 ## 方法。

require.resolve は、Module._resolveFileName メソッド、

// node.js内置模块require的源代码
function resolve(request, options) {
  validateString(request, &#39;request&#39;);
  return Module._resolveFilename(request, mod, false, options); //核心实现
}

require.resolve = resolve;

function paths(request) {
  validateString(request, &#39;request&#39;);
  return Module._resolveLookupPaths(request, mod); //核心代码
}

resolve.paths = paths;

  • Module._resolveFileName# を通じて実装されます。 ##コア プロセス

パスが組み込みモジュールかどうかを判断しますそうでない場合は、Module._resolveLookupPahts メソッドを使用しますパスを結合し、環境内のパスを結合します。

モジュールの実際のパスをクエリします。
    Module._findPath
  • return Module._resolveFilename(request 、親、isMain) ;
  • Module._resolveFilename = function(request, parent, isMain, options) {
      if (NativeModule.canBeRequiredByUsers(request)) { //是否为内置模块
        return request;
      }
    
      let paths;
      // 让paths和环境变量中的paths结合
      paths = Module._resolveLookupPaths(request, parent); //核心代码
      
      if (parent && parent.filename) {
        // 读取filename对应的package.json文件,看是否有exports字段,当前filename = false
        const filename = trySelf(parent.filename, request);
        if (filename) { //false
          const cacheKey = request + &#39;\x00&#39; +
              (paths.length === 1 ? paths[0] : paths.join(&#39;\x00&#39;));
          Module._pathCache[cacheKey] = filename;
          return filename;
        }
      }
    
     //关键代码,找到本地执行文件 // Look up the filename first, since that&#39;s the cache key. 
      const filename = Module._findPath(request, paths, isMain, false);
      if (filename) return filename;
      // ...
    };

    Module._resolveLookupPahts方法

    • 生成要查找模块的所有路径上可能存在node_modules的路径数组
    • require.resolve.paths("yargs")核心实现方法

    生成

    [
      &#39;/Users/rainbow/Documents/前端/脚手架开发/rainbow-test/bin/node_modules&#39;,
      &#39;/Users/rainbow/Documents/前端/脚手架开发/rainbow-test/node_modules&#39;,
      &#39;/Users/rainbow/Documents/前端/脚手架开发/node_modules&#39;,
      &#39;/Users/rainbow/Documents/前端/node_modules&#39;,
      &#39;/Users/rainbow/Documents/node_modules&#39;,
      &#39;/Users/rainbow/node_modules&#39;,
      &#39;/Users/node_modules&#39;,
      &#39;/node_modules&#39;,
      &#39;/Users/rainbow/.node_modules&#39;,
      &#39;/Users/rainbow/.node_libraries&#39;,
      &#39;/usr/local/Cellar/node/14.3.0_1/lib/node&#39;
    ]

    Node.js でのモジュール パス分析について説明する記事

    Module._resolveLookupPaths = function(request, parent) {
      if (NativeModule.canBeRequiredByUsers(request)) {
        debug(&#39;looking for %j in []&#39;, request);
        return null;
      }
    
      // Check for node modules paths.
      if (request.charAt(0) !== &#39;.&#39; ||
          (request.length > 1 &&
          request.charAt(1) !== &#39;.&#39; &&
          request.charAt(1) !== &#39;/&#39; &&
          (!isWindows || request.charAt(1) !== &#39;\&#39;))){
         let paths = modulePaths;
         if (parent != null && parent.paths && parent.paths.length) {
          paths = parent.paths.concat(paths);
        }
    
        debug(&#39;looking for %j in %j&#39;, request, paths);
        return paths.length > 0 ? paths : null;
      }
      
      // In REPL, parent.filename is null.
      if (!parent || !parent.id || !parent.filename) {
        // Make require(&#39;./path/to/foo&#39;) work - normally the path is taken
        // from realpath(__filename) but in REPL there is no filename
        const mainPaths = [&#39;.&#39;];
    
        debug(&#39;looking for %j in %j&#39;, request, mainPaths);
        return mainPaths;
      }
    
      debug(&#39;RELATIVE: requested: %s from parent.id %s&#39;, request, parent.id);
    
      const parentDir = [path.dirname(parent.filename)];
      debug(&#39;looking for %j&#39;, parentDir);
      return parentDir;
    };

    Module._findPath核心流程

    • 查询缓存(将request和paths通过\x00合并生成cacheKey)(\x00是空格的16进制)
    • 遍历Module._resolveLookupPahts方法生成的paths数组,将pathrequest组成文件路径basePath
    • 如果basePath存在则调用fs.realPahtSync获取文件的真实路径

    Node.js でのモジュール パス分析について説明する記事

    fs.realPahtSync

    Node.js でのモジュール パス分析について説明する記事

    更多node相关知识,请访问:nodejs 教程!!

以上がNode.js でのモジュール パス分析について説明する記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。