Ein Artikel über die Modulpfadanalyse in Node.js
Dieser Artikel führt Sie durch die Modulpfadanalyse in Node.js und stellt die Node-Modulpfadanalysemethode vor.
require
Fall
- Es gibt derzeit ein Projekt
- Aktueller Projektpfad
/Benutzer/ rainbow /Documents/front-end/scaffolding development/rainbow-test
- Es gibt eine Reihe von Dateien im Projekt-Bin-Verzeichnis
require
案例- 当前有一个项目
- 当前项目路径
/Users/rainbow/Documents/前端/脚手架开发/rainbow-test
- 项目bin目录下有一堆文件
- /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数组,将path
与request
组成文件路径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
方法
// 进入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循环的核心算法解析:
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);
- /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' ]
requireDer Prozess des Parsens und Findens von Modulausführungsdateien
require implementiert. Auflösung
-Methode. 🎜- require.resolve wird durch die Methode
Module._resolveFileName
implementiert Module._resolveFileName
Der Kernprozess ist:- < li>Bestimmen Sie, ob der Pfad ein integriertes Modul ist
- Wenn nicht, verwenden Sie die Methode
Module._resolveLookupPahts
, um mögliche Pfade für node_modules zu generieren. Wenn der eingehende Pfad „/“ ist. test/lerna /cli.js‘, fügen Sie das Pfadarray vonnode_moduls
unter jeder Pfadebene hinzu - Fragen Sie den tatsächlichen Pfad des Moduls über
Module._findPath</ ab. code>, < /li></ul></li></ul>🎜2. <code>Module._findPath
Der Kernprozess ist: 🎜- Abfragecache (Anfrage übergeben und Pfade durch
x00
Zusammenführen, umcacheKey
zu generieren) - Durchlaufen Sie das von der Methode
Module._resolveLookupPahts
generierte Pfadarray und vergleichen Sie espath
mitrequest
bildet den Dateipfad basePath - Wenn
basePath
vorhanden ist, rufen Siefs.realPahtSync</code auf > um den tatsächlichen Pfad der Datei zu erhalten</li> <li>Zwischenspeichern Sie den tatsächlichen Pfad der Datei in <code>Module._pathCache
(Schlüssel ist CacheKey) (Module._pathCache ist eine Karte)
fs.realPahtSync< /code>Kernprozess: 🎜<ul><li>Cache abfragen (der Cache-Schlüssel ist p. Das ist der in Module._findPath generierte Pfad)</li ><li>Durchlaufen Sie die Pfadzeichenfolge von links nach rechts und fragen Sie / ab. Teilen Sie den Pfad auf und bestimmen Sie, ob es sich um einen Softlink handelt. Fragen Sie den tatsächlichen Link ab und generieren Sie dann einen neuen Pfad Weiter durchqueren. Hier ist ein Detail: </li><li>Während des Durchquerungsprozesses wird die generierte Unterpfadbasis in KnownHard und zwischengespeichert, um wiederholte Abfragen zu vermeiden</li><li>Nachdem die Durchquerung abgeschlossen ist, Der dem Modul entsprechende tatsächliche Pfad wird als Schlüssel und der tatsächliche Pfad als Wert verwendet und im Cache gespeichert </li></ul>🎜4 >require.resolve.paths
entsprichtModule._resolveLookupPaths
. Diese Methode ruft alle möglichen Pfade von node_modules ab, um ein Array zu bilden. 🎜🎜5. Das Implementierungsprinzip vonrequire.resolve.paths
lautet: 🎜- Wenn es
/
(Stammpfad) ist, geben Siedirekt zurück ['/node_modules ']
- Andernfalls durchlaufen Sie die Pfadzeichenfolge von hinten nach vorne, teilen Sie den Pfad, fügen Sie am Ende node_modules hinzu und übergeben Sie ein Pfadarray bis Die Abfrage wird nicht mehr zurückgegeben. Geben Sie das Pfadarray zurück
require('yargs')< verwenden /code>🎜 🎜<span style="max-width:90%">🎜require method</strong></span>🎜<ul><li>Die eigentliche Verwendung ist der <code>Module._load</code > method</li ></ul><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>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; };</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>rrree🎜🎜<code>Module._nodeModulePaths
method🎜🎜🎜rrreee🎜Kernalgorithmusanalyse der for-Schleife:🎜🎜
🎜🎜🎜
Module._load< /code>Methode</strong></span>🎜🎜<code>Module._load(id, this, /* isMain */ false)
🎜🎜Der Kernimplementierungscode ist: < code>const filename = Module ._resolveFilename(request, parent, isMain);🎜🎜🎜require.resolve
🎜🎜 Die Pfadauflösung desNode.js
-Projektmoduls wird durch die Methoderequire.resolve
implementiert. 🎜- require.resolve wird durch die Methode
Module._resolveFileName
implementiert,
Module._resolveFileName
Kernprozess🎜- Bestimmen Sie, ob der Pfad ein integriertes Modul ist
- Wenn nicht, übergeben Sie
Module._resolveLookupPahts
-Methode, kombiniert Pfade mit Pfaden in der Umgebung - Fragen Sie den tatsächlichen Pfad des Moduls über
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 + '\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; // ... };
Nach dem Login kopierenNach dem Login kopierenModule._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' ]
Nach dem Login kopierenNach dem Login kopierenModule._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; };
Nach dem Login kopierenNach dem Login kopierenModule._findPath
核心流程- 查询缓存(将request和paths通过
\x00
合并生成cacheKey
)(\x00
是空格的16进制) - 遍历
Module._resolveLookupPahts
方法生成的paths
数组,将path
与request
组成文件路径basePath
- 如果basePath存在则调用
fs.realPahtSync
获取文件的真实路径
fs.realPahtSync
更多node相关知识,请访问:nodejs 教程!!
Das obige ist der detaillierte Inhalt vonEin Artikel über die Modulpfadanalyse in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!
- Abfragecache (Anfrage übergeben und Pfade durch

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Dieser Artikel vermittelt Ihnen ein detailliertes Verständnis des Speichers und Garbage Collectors (GC) der NodeJS V8-Engine. Ich hoffe, er wird Ihnen hilfreich sein!

Der nicht blockierende und ereignisgesteuerte Knotendienst hat den Vorteil eines geringen Speicherverbrauchs und eignet sich sehr gut für die Verarbeitung massiver Netzwerkanforderungen. Unter der Voraussetzung massiver Anfragen müssen Probleme im Zusammenhang mit der „Speicherkontrolle“ berücksichtigt werden. 1. Der Garbage-Collection-Mechanismus und die Speicherbeschränkungen von V8 Js wird von der Garbage-Collection-Maschine gesteuert

Die Auswahl eines Docker-Images für Node mag trivial erscheinen, aber die Größe und potenziellen Schwachstellen des Images können erhebliche Auswirkungen auf Ihren CI/CD-Prozess und Ihre Sicherheit haben. Wie wählen wir also das beste Node.js-Docker-Image aus?

Das Dateimodul ist eine Kapselung der zugrunde liegenden Dateioperationen, wie z. B. Lesen/Schreiben/Öffnen/Schließen/Löschen von Dateien, Hinzufügen usw. Das größte Merkmal des Dateimoduls besteht darin, dass alle Methoden zwei Versionen von **synchronem** und **bereitstellen. asynchron**, mit Methoden mit dem Suffix sync sind alle Synchronisationsmethoden, und diejenigen ohne sind alle heterogene Methoden.

Node 19 wurde offiziell veröffentlicht. Dieser Artikel wird Ihnen eine detaillierte Erklärung der 6 Hauptfunktionen von Node.js 19 geben. Ich hoffe, er wird Ihnen hilfreich sein!

Wie führt Node.js GC (Garbage Collection) durch? Der folgende Artikel führt Sie durch.

Die Ereignisschleife ist ein grundlegender Bestandteil von Node.js und ermöglicht die asynchrone Programmierung, indem sie sicherstellt, dass der Hauptthread nicht blockiert wird. Das Verständnis der Ereignisschleife ist für die Erstellung effizienter Anwendungen von entscheidender Bedeutung. Der folgende Artikel wird Ihnen ein detailliertes Verständnis der Ereignisschleife in Node vermitteln. Ich hoffe, er wird Ihnen hilfreich sein!

Wie packe ich die ausführbare Datei von nodejs mit pkg? Im folgenden Artikel erfahren Sie, wie Sie mit pkg ein Node-Projekt in eine ausführbare Datei packen. Ich hoffe, dass er Ihnen weiterhilft!
