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

青灯夜游
リリース: 2021-12-16 19:19:31
転載
1858 人が閲覧しました

この記事では、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

#

// 进入mac电脑所在的逻辑:
// from => /Users/rainbow/Documents/前端/脚手架开发/lerna源码/lernas  //'from' 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: [ '//node_modules', '/node_modules' ]
    if (from === '/')
      return ['/node_modules'];

    const paths = [];
    
   // 关键算法代码
    for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) {
      const code = from.charCodeAt(i);
      if (code === CHAR_FORWARD_SLASH) {
        if (p !== nmLen)
          paths.push(from.slice(0, last) + '/node_modules');
        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('/node_modules');

    return paths;
  };
ログイン後にコピー
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, 'request');
  return Module._resolveFilename(request, mod, false, options); //核心实现
}

require.resolve = resolve;

function paths(request) {
  validateString(request, 'request');
  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 + '\x00' +
              (paths.length === 1 ? paths[0] : paths.join('\x00'));
          Module._pathCache[cacheKey] = filename;
          return filename;
        }
      }
    
     //关键代码,找到本地执行文件 // Look up the filename first, since that's the cache key. 
      const filename = Module._findPath(request, paths, isMain, false);
      if (filename) return filename;
      // ...
    };
    ログイン後にコピー

    Module._resolveLookupPahts方法

    • 生成要查找模块的所有路径上可能存在node_modules的路径数组
    • 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'
    ]
    ログイン後にコピー

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

    Module._resolveLookupPaths = function(request, parent) {
      if (NativeModule.canBeRequiredByUsers(request)) {
        debug('looking for %j in []', request);
        return null;
      }
    
      // Check for node modules paths.
      if (request.charAt(0) !== '.' ||
          (request.length > 1 &&
          request.charAt(1) !== '.' &&
          request.charAt(1) !== '/' &&
          (!isWindows || request.charAt(1) !== '\'))){
         let paths = modulePaths;
         if (parent != null && parent.paths && parent.paths.length) {
          paths = parent.paths.concat(paths);
        }
    
        debug('looking for %j in %j', request, paths);
        return paths.length > 0 ? paths : null;
      }
      
      // In REPL, parent.filename is null.
      if (!parent || !parent.id || !parent.filename) {
        // Make require('./path/to/foo') work - normally the path is taken
        // from realpath(__filename) but in REPL there is no filename
        const mainPaths = ['.'];
    
        debug('looking for %j in %j', request, mainPaths);
        return mainPaths;
      }
    
      debug('RELATIVE: requested: %s from parent.id %s', request, parent.id);
    
      const parentDir = [path.dirname(parent.filename)];
      debug('looking for %j', 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 までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!