목차
require사례
require解析并找到模块执行文件的流程
require使用到内置模块的方法
웹 프론트엔드 JS 튜토리얼 Node.js의 모듈 경로 분석에 대해 이야기하는 기사

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

Dec 16, 2021 pm 07:19 PM
node.js

이 글은 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Node V8 엔진의 메모리와 GC에 대한 자세한 그래픽 설명 Node V8 엔진의 메모리와 GC에 대한 자세한 그래픽 설명 Mar 29, 2023 pm 06:02 PM

이 기사는 NodeJS V8 엔진의 메모리 및 가비지 수집기(GC)에 대한 심층적인 이해를 제공할 것입니다. 도움이 되기를 바랍니다.

Node의 메모리 제어에 관한 기사 Node의 메모리 제어에 관한 기사 Apr 26, 2023 pm 05:37 PM

Non-Blocking, Event-Driven 기반으로 구축된 Node 서비스는 메모리 소모가 적다는 장점이 있으며, 대규모 네트워크 요청을 처리하는데 매우 적합합니다. 대규모 요청을 전제로 '메모리 제어'와 관련된 문제를 고려해야 합니다. 1. V8의 가비지 수집 메커니즘과 메모리 제한 Js는 가비지 수집 기계에 의해 제어됩니다.

최고의 Node.js Docker 이미지를 선택하는 방법에 대해 이야기해 볼까요? 최고의 Node.js Docker 이미지를 선택하는 방법에 대해 이야기해 볼까요? Dec 13, 2022 pm 08:00 PM

Node용 Docker 이미지를 선택하는 것은 사소한 문제처럼 보일 수 있지만 이미지의 크기와 잠재적인 취약점은 CI/CD 프로세스와 보안에 상당한 영향을 미칠 수 있습니다. 그렇다면 최고의 Node.js Docker 이미지를 어떻게 선택합니까?

Node의 파일 모듈에 대해 자세히 이야기해 보겠습니다. Node의 파일 모듈에 대해 자세히 이야기해 보겠습니다. Apr 24, 2023 pm 05:49 PM

파일 모듈은 파일 읽기/쓰기/열기/닫기/삭제 추가 등과 같은 기본 파일 작업을 캡슐화한 것입니다. 파일 모듈의 가장 큰 특징은 모든 메소드가 **동기** 및 ** 두 가지 버전을 제공한다는 것입니다. 비동기**, sync 접미사가 있는 메서드는 모두 동기화 메서드이고, 없는 메서드는 모두 이기종 메서드입니다.

Node.js 19가 공식적으로 출시되었습니다. Node.js의 6가지 주요 기능에 대해 이야기해 보겠습니다! Node.js 19가 공식적으로 출시되었습니다. Node.js의 6가지 주요 기능에 대해 이야기해 보겠습니다! Nov 16, 2022 pm 08:34 PM

Node 19가 정식 출시되었습니다. 이 글에서는 Node.js 19의 6가지 주요 기능에 대해 자세히 설명하겠습니다. 도움이 되셨으면 좋겠습니다!

Node.js의 GC(가비지 수집) 메커니즘에 대해 이야기해 보겠습니다. Node.js의 GC(가비지 수집) 메커니즘에 대해 이야기해 보겠습니다. Nov 29, 2022 pm 08:44 PM

Node.js는 GC(가비지 수집)를 어떻게 수행하나요? 다음 기사에서는 이에 대해 설명합니다.

Node의 이벤트 루프에 대해 이야기해 봅시다. Node의 이벤트 루프에 대해 이야기해 봅시다. Apr 11, 2023 pm 07:08 PM

이벤트 루프는 Node.js의 기본 부분이며 메인 스레드가 차단되지 않도록 하여 비동기 프로그래밍을 가능하게 합니다. 이벤트 루프를 이해하는 것은 효율적인 애플리케이션을 구축하는 데 중요합니다. 다음 기사는 Node.js의 이벤트 루프에 대한 심층적인 이해를 제공할 것입니다. 도움이 되기를 바랍니다!

pkg를 사용하여 Node.js 프로젝트를 실행 파일로 패키징하는 방법에 대해 이야기해 보겠습니다. pkg를 사용하여 Node.js 프로젝트를 실행 파일로 패키징하는 방법에 대해 이야기해 보겠습니다. Dec 02, 2022 pm 09:06 PM

nodejs 실행 파일을 pkg로 패키징하는 방법은 무엇입니까? 다음 기사에서는 pkg를 사용하여 Node 프로젝트를 실행 파일로 패키징하는 방법을 소개합니다. 도움이 되기를 바랍니다.

See all articles