Table des matières
requireCase
require解析并找到模块执行文件的流程
require使用到内置模块的方法
Maison interface Web js tutoriel Un article pour parler de l'analyse du chemin du module dans Node.js

Un article pour parler de l'analyse du chemin du module dans Node.js

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

Cet article vous guidera à travers l'analyse du chemin du module dans Node.js et présentera la méthode d'analyse du chemin du module Node J'espère qu'il vous sera utile !

Un article pour parler de l'analyse du chemin du module dans Node.js

requireCase

  • Il y a actuellement un projet
  • Chemin actuel du projet /Utilisateurs/ Rainbow /Documents/front-end/scaffolding development/rainbow-test
  • Il y a un tas de fichiers dans le répertoire bin du projet
require案例
  • 当前有一个项目
  • 当前项目路径/Users/rainbow/Documents/前端/脚手架开发/rainbow-test
  • 项目bin目录下有一堆文件

Un article pour parler de lanalyse du chemin du module dans 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' ] 输出的文件可能在的路径的数组
Copier après la connexion
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'
]
*/
Copier après la connexion

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--;
  }
};
Copier après la connexion
// 参数
id = 'yargs'
this={
 paths: Module._nodeModulePaths(process.cwd())
}
Copier après la connexion

Module._nodeModulePaths方法

Un article pour parler de lanalyse du chemin du module dans 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;
  };
Copier après la connexion

for循环的核心算法解析:

Un article pour parler de lanalyse du chemin du module dans 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;
Copier après la connexion

Module._resolveFileName核心流程

  • 判断该路径是否是内置模块
  • 不是,则通过Module._resolveLookupPahts方法,将paths和环境中的路径结合起来
  • 通过Module._findPath查询模块的真实路径

return Module._resolveFilename(request, parent, isMain);Un article pour parler de lanalyse du chemin du module dans 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;
  // ...
};
Copier après la connexion
Copier après la connexion
[
  '/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'
]
Copier après la connexion
Copier après la connexion

Un article pour parler de lanalyse du chemin du module dans Node.jsrequireLe processus d'analyse et de recherche des fichiers d'exécution de module

1 🎜Nodejs🎜la résolution du chemin du module du projet est implémentée via le require. méthode de résolution. 🎜
  • require.resolve est implémenté via la méthode Module._resolveFileName
  • Module._resolveFileNameLe processus principal est :
      < li>Déterminez si le chemin est un module intégré
    • Sinon, utilisez la méthode Module._resolveLookupPahts pour générer des chemins possibles pour node_modules Si le chemin entrant est '/. test/lerna /cli.js', ajoutez le tableau de chemins de node_modules sous chaque niveau de chemin
    • Interrogez le chemin réel du module via Module._findPath</ code>, < /li></ul></li></ul>🎜2. <code>Module._findPath Le processus principal est : 🎜
      • Cache de requête (demande de réussite et chemins via x00Fusionner pour générer cacheKey)
      • Parcourez le tableau de chemins généré par la méthode Module._resolveLookupPahts et comparez path avec request compose le chemin du fichier basePath
      • Si basePath existe, appelez fs.realPahtSync</code > pour obtenir le chemin réel du fichier</li> <li>Mettre en cache le chemin réel du fichier dans <code>Module._pathCache (la clé est cacheKey) (Module._pathCache est une map)
      🎜3. fs.realPahtSync< /code>Processus principal : 🎜<ul><li>Cache de requête (la clé du cache est p. C'est le chemin généré dans Module._findPath)</li ><li>Parcourez la chaîne du chemin de gauche à droite et interrogez / When, divisez le chemin et déterminez si le chemin est un lien symbolique, interrogez le lien réel et générez un nouveau chemin p, puis. continuez à parcourir. Voici un détail : </li><li>Pendant le processus de traversée, la base de sous-chemin générée sera mise en cache dans knownHard et dans le cache pour éviter les requêtes répétées</li><li>Une fois la traversée terminée, le chemin réel correspondant au module est obtenu. A ce moment, le chemin d'origine sera utilisé comme clé et le chemin réel comme valeur, et enregistré dans le cache </li></ul>🎜4. >require.resolve.paths est équivalent à Module._resolveLookupPaths Cette méthode obtient tous les chemins possibles de node_modules pour former un tableau. 🎜🎜5. Le principe d'implémentation de require.resolve.paths est : 🎜
      • S'il s'agit de / (chemin racine), renvoyez directement ['/node_modules ']
      • Sinon, parcourez la chaîne de chemin d'arrière en avant. Lorsque / est interrogé, divisez le chemin, ajoutez node_modules à la fin et transmettez un tableau de chemins jusqu'à ce que la requête n'est plus Renvoyer le tableau des chemins à/après
      🎜🎜require en utilisant la méthode du module intégré🎜Quand on utilise require(' yargs')🎜 🎜🎜méthode require🎜
      • L'utilisation réelle est le Module. Méthode _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;
      };
      Copier après la connexion
      Copier après la connexion
      rrree🎜🎜Module._nodeModulePathsméthode🎜🎜Un article pour parler de lanalyse du chemin du module dans Node.js🎜rrreee🎜Analyse de l'algorithme de base de la boucle for :🎜🎜Un article pour parler de lanalyse du chemin du module dans Node.js🎜🎜🎜Module._load< /code>Méthode</strong></span>🎜🎜<code>Module._load(id, this, /* isMain */ false)🎜🎜L'implémentation principale le code est : const filename = Module ._resolveFilename(request, parent, isMain);🎜🎜🎜require.resolve 🎜🎜 La résolution du chemin du module de projet Node.js est implémentée via la méthode require.resolve. 🎜
      • require.resolve est implémenté via la méthode Module._resolveFileName,
      rrreee🎜🎜 Module._resolveFileNameCore Process🎜
      • Déterminez si le chemin est un module intégré
      • Sinon, transmettez Méthode Module. _resolveLookupPahts, combine les chemins avec les chemins dans l'environnement
      • Interrogez le chemin réel du module via 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;
        // ...
      };
      Copier après la connexion
      Copier après la connexion

      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;
      ]
      Copier après la connexion
      Copier après la connexion

      Un article pour parler de lanalyse du chemin du module dans 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;
      };
      Copier après la connexion
      Copier après la connexion

      Module._findPath核心流程

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

      Un article pour parler de lanalyse du chemin du module dans Node.js

      fs.realPahtSync

      Un article pour parler de lanalyse du chemin du module dans Node.js

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

      Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Explication graphique détaillée de la mémoire et du GC du moteur Node V8 Explication graphique détaillée de la mémoire et du GC du moteur Node V8 Mar 29, 2023 pm 06:02 PM

Cet article vous donnera une compréhension approfondie de la mémoire et du garbage collector (GC) du moteur NodeJS V8. J'espère qu'il vous sera utile !

Un article pour parler du contrôle de la mémoire dans Node Un article pour parler du contrôle de la mémoire dans Node Apr 26, 2023 pm 05:37 PM

Le service Node construit sur une base non bloquante et piloté par les événements présente l'avantage d'une faible consommation de mémoire et est très adapté à la gestion de requêtes réseau massives. Dans le contexte de demandes massives, les questions liées au « contrôle de la mémoire » doivent être prises en compte. 1. Le mécanisme de récupération de place du V8 et les limitations de mémoire Js sont contrôlés par la machine de récupération de place

Parlons de la façon de choisir la meilleure image Docker Node.js ? Parlons de la façon de choisir la meilleure image Docker Node.js ? Dec 13, 2022 pm 08:00 PM

Le choix d'une image Docker pour Node peut sembler trivial, mais la taille et les vulnérabilités potentielles de l'image peuvent avoir un impact significatif sur votre processus CI/CD et votre sécurité. Alors, comment choisir la meilleure image Docker Node.js ?

Parlons en profondeur du module File dans Node Parlons en profondeur du module File dans Node Apr 24, 2023 pm 05:49 PM

Le module de fichiers est une encapsulation des opérations de fichiers sous-jacentes, telles que l'ajout de lecture/écriture/ouverture/fermeture/suppression de fichiers, etc. La plus grande caractéristique du module de fichiers est que toutes les méthodes fournissent deux versions de **synchrone** et ** asynchrone**, with Les méthodes avec le suffixe sync sont toutes des méthodes de synchronisation, et celles qui n'en ont pas sont toutes des méthodes hétérogènes.

Node.js 19 est officiellement sorti, parlons de ses 6 fonctionnalités majeures ! Node.js 19 est officiellement sorti, parlons de ses 6 fonctionnalités majeures ! Nov 16, 2022 pm 08:34 PM

Node 19 est officiellement publié. Cet article vous donnera une explication détaillée des 6 fonctionnalités majeures de Node.js 19. J'espère qu'il vous sera utile !

Parlons du mécanisme GC (garbage collection) dans Node.js Parlons du mécanisme GC (garbage collection) dans Node.js Nov 29, 2022 pm 08:44 PM

Comment Node.js fait-il le GC (garbage collection) ? L’article suivant vous guidera à travers cela.

Parlons de la boucle d'événements dans Node Parlons de la boucle d'événements dans Node Apr 11, 2023 pm 07:08 PM

La boucle d'événements est un élément fondamental de Node.js et permet une programmation asynchrone en garantissant que le thread principal n'est pas bloqué. Comprendre la boucle d'événements est crucial pour créer des applications efficaces. L'article suivant vous donnera une compréhension approfondie de la boucle d'événements dans Node. J'espère qu'il vous sera utile !

Parlons de la façon d'utiliser pkg pour empaqueter des projets Node.js dans des fichiers exécutables. Parlons de la façon d'utiliser pkg pour empaqueter des projets Node.js dans des fichiers exécutables. Dec 02, 2022 pm 09:06 PM

Comment empaqueter le fichier exécutable nodejs avec pkg ? L'article suivant vous expliquera comment utiliser pkg pour empaqueter un projet Node dans un fichier exécutable. J'espère qu'il vous sera utile !

See all articles