> 웹 프론트엔드 > JS 튜토리얼 > Node.js의 모듈 경로 분석에 대해 이야기하는 기사

Node.js의 모듈 경로 분석에 대해 이야기하는 기사

青灯夜游
풀어 주다: 2021-12-16 19:19:31
앞으로
2073명이 탐색했습니다.

이 글은 Node.js의 모듈 경로 분석을 안내하고 Node 모듈 경로 분석 방법을 소개합니다. 도움이 되길 바랍니다!

Node.js의 모듈 경로 분석에 대해 이야기하는 기사

require사례

  • 현재 프로젝트가 있습니다
  • 현재 프로젝트 경로 /Users/ Rainbow /Documents/Front-end/Scaffolding Development/rainbow-test
  • 프로젝트 bin 디렉토리에 많은 파일이 있습니다
require案例
  • 当前有一个项目
  • 当前项目路径/Users/rainbow/Documents/前端/脚手架开发/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核心流程是:
    • 判断该路径是否是内置模块
    • 不是,则通过Module._resolveLookupPahts方法,生成node_modules可能存在的路径,如果传入的路径是’/test/lerna/cli.js’,在每一级路径下加上node_moduels的路径数组
    • 通过Module._findPath查询模块的真实路径,

2、Module._findPath核心流程是:

  • 查询缓存(将request和paths通过x00合并生成cacheKey)
  • 遍历 Module._resolveLookupPahts方法生成的paths数组,将pathrequest组成文件路径basePath
  • 如果basePath存在则调用fs.realPahtSync获取文件的真实路径
  • 将文件真实路径缓存到Module._pathCache(key为cacheKey)(Module._pathCache就是一个map)

3、fs.realPahtSync核心流程:

  • 查询缓存(缓存的key为p。即Module._findPath中生成的路径)
  • 从左往右遍历路径字符串,查询到/时,拆分路径,判断该路径是否为软链接,如果是软链接则查询真实链接,并生成新路径p,然后继续让后遍历,这里有一个细节:
  • 遍历过程中生成的子路径base会缓存在knownHard和cache中,避免重复查询
  • 遍历完成得到模块对应的真实路径,此时会将原始路径original作为key,真实路径作为value,保存到缓存中

4、require.resolve.paths等价于Module._resolveLookupPaths,该方法获取所有node_modules可能存在的路径组成一个数组。

5、require.resolve.paths实现原理是:

  • 如果是/(根路径)直接返回['/node_modules']
  • 否则,将路径字符串从后往前遍历,查询到/时,拆分路径,在后面加上node_modules,并传入一个paths数组,直到查询不到/后返回paths数组

require使用到内置模块的方法

当我们使用require('yargs')

require方法

  • 实际使用的是Module._load方法
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._nodeModulePaths方法

Node.js의 모듈 경로 분석에 대해 이야기하는 기사

// 进入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._load方法

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方法,将paths和环境中的路径结合起来
  • 通过Module._findPath查询模块的真实路径

return Module._resolveFilename(request, parent, isMain);Node.js의 모듈 경로 분석에 대해 이야기하는 기사

  • /bin/ index.js
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;
  // ...
};
로그인 후 복사
로그인 후 복사
[
  '/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의 모듈 경로 분석에 대해 이야기하는 기사require모듈 실행 파일을 구문 분석하고 찾는 프로세스

1. 🎜Nodejs🎜프로젝트 모듈 경로 확인은 require를 통해 구현됩니다. 해결 메소드. 🎜
  • require.resolve는 Module._resolveFileName 메소드를 통해 구현됩니다.
  • Module._resolveFileName핵심 프로세스는 다음과 같습니다.
      < li>경로가 내장 모듈인지 확인
    • 그렇지 않은 경우 Module._resolveLookupPahts 메서드를 사용하여 node_modules에 대한 가능한 경로를 생성합니다. test/lerna /cli.js', 각 경로 수준 아래에 node_moduels의 경로 배열을 추가합니다.
    • Module._findPath</를 통해 모듈의 실제 경로를 쿼리합니다. code>, < /li></ul></li></ul>🎜2. <code>Module._findPath 핵심 프로세스는 다음과 같습니다. 🎜
      • 쿼리 캐시(요청 통과 및 x00병합을 통해 cacheKey 생성)
      • Module._resolveLookupPahts 메서드에 의해 생성된 경로 배열을 탐색하고 비교합니다. request가 있는 path는 파일 경로 basePath를 구성합니다.
      • basePath가 존재하는 경우 fs.realPahtSync</code를 호출합니다. > 파일의 실제 경로를 얻으려면</li> <li>파일의 실제 경로를 <code>Module._pathCache에 캐시하세요(키는 캐시키입니다)(Module._pathCache는 맵입니다)
      🎜3. fs.realPahtSync핵심 프로세스: 🎜
      • 쿼리 캐시(캐시 키는 p입니다. Module._findPath에서 생성된 경로입니다)
      • 경로 문자열을 왼쪽에서 오른쪽으로 순회하여 질의 / 언제, 경로를 분할하여 경로가 소프트 링크인지 확인하고, 실제 링크를 질의하여 새로운 경로 p를 생성한다. 자세한 내용은 다음과 같습니다.
      • 순회 프로세스 중에 생성된 하위 경로 기반은 반복되는 쿼리를 피하기 위해 KnownHard 및 캐시에 캐시됩니다.
      • 순회가 완료된 후, 이때 모듈에 해당하는 실제 경로가 키로 사용되고 실제 경로가 값으로 사용되어 캐시에 저장됩니다. >require.resolve.paths는 Module._resolveLookupPaths와 동일합니다. 이 메서드는 node_modules의 가능한 모든 경로를 얻어 배열을 형성합니다. 🎜🎜5. require.resolve.paths의 구현 원칙은 다음과 같습니다. 🎜
        • /(루트 경로)인 경우 를 직접 반환합니다. ['/node_modules ']
        • 그렇지 않으면 경로 문자열을 뒤에서 앞으로 이동합니다. /가 쿼리되면 경로를 분할하고 끝에 node_modules를 추가한 다음 경로 배열을 전달합니다. 쿼리는 더 이상 내장 모듈의 메서드를 사용하여
        🎜🎜require 경로 배열을 반환하지 않습니다.🎜require(' yargs')🎜 🎜🎜require 메소드🎜
        • 실제 용도는 모듈입니다. _load 메소드
        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;
        };
        로그인 후 복사
        로그인 후 복사
        rrree🎜🎜Module._nodeModulePaths메서드🎜🎜Node.js의 모듈 경로 분석에 대해 이야기하는 기사🎜rrreee🎜for 루프의 핵심 알고리즘 분석:🎜🎜Node.js의 모듈 경로 분석에 대해 이야기하는 기사🎜🎜🎜Module._load< /code>메서드</strong></span>🎜🎜<code>Module._load(id, this, /* isMain */ false)🎜🎜핵심 구현 코드는 다음과 같습니다: const filename = Module ._resolveFilename(request, parent, isMain);🎜🎜🎜require.resolve 🎜🎜 Node.js 프로젝트 모듈 경로 확인은 require.resolve 메서드를 통해 구현됩니다. 🎜
        • require.resolve는 Module._resolveFileName 메서드를 통해 구현됩니다.
        rrreee🎜🎜 Module._resolveFileName핵심 프로세스🎜
        • 경로가 내장 모듈인지 확인
        • 그렇지 않은 경우 Module._resolveLookupPahts 메소드는 경로를 환경의 경로와 결합합니다.
        • Module._findPath를 통해 모듈의 실제 경로를 쿼리합니다.
        🎜return Module._resolveFilename(request, parent, 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿