Dieser Artikel führt Sie durch die Modulpfadanalyse in Node.js und stellt die Node-Modulpfadanalysemethode vor.
require
Fall/Benutzer/ rainbow /Documents/front-end/scaffolding development/rainbow-test
require
案例/Users/rainbow/Documents/前端/脚手架开发/rainbow-test
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' ] */
1、Nodejs项目模块路径解析是通过require.resolve
方式实现的。
Module._resolveFileName
方法实现的Module._resolveFileName
核心流程是:Module._resolveLookupPahts
方法,生成node_modules可能存在的路径,如果传入的路径是’/test/lerna/cli.js’,在每一级路径下加上node_moduels
的路径数组Module._findPath
查询模块的真实路径,2、Module._findPath
核心流程是:
x00
合并生成cacheKey
)Module._resolveLookupPahts
方法生成的paths数组,将path
与request
组成文件路径basePathbasePath
存在则调用fs.realPahtSync
获取文件的真实路径Module._pathCache
(key为cacheKey)(Module._pathCache就是一个map)3、fs.realPahtSync
核心流程:
4、require.resolve.paths
等价于Module._resolveLookupPaths
,该方法获取所有node_modules可能存在的路径组成一个数组。
5、require.resolve.paths
实现原理是:
/
(根路径)直接返回['/node_modules']
当我们使用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
方式实现的。
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);
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
1. 🎜Nodejs🎜Die Projektmodulpfadauflösung wird durchrequire implementiert. Auflösung
-Methode. 🎜Module._resolveFileName
implementiertModule._resolveFileName
Der Kernprozess ist: 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 von node_moduls
unter jeder Pfadebene hinzuModule._findPath</ ab. code>, < /li></ul></li></ul>🎜2. <code>Module._findPath
Der Kernprozess ist: 🎜 x00
Zusammenführen, um cacheKey
zu generieren)Module._resolveLookupPahts
generierte Pfadarray und vergleichen Sie es path
mit request
bildet den Dateipfad basePathbasePath
vorhanden ist, rufen Sie fs.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
entspricht Module._resolveLookupPaths
. Diese Methode ruft alle möglichen Pfade von node_modules ab, um ein Array zu bilden. 🎜🎜5. Das Implementierungsprinzip von require.resolve.paths
lautet: 🎜/
(Stammpfad) ist, geben Sie direkt zurück ['/node_modules ']
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:js;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 des Node.js
-Projektmoduls wird durch die Methode require.resolve
implementiert. 🎜Module._resolveFileName
implementiert, Module._resolveFileName
Kernprozess🎜Module._resolveLookupPahts
-Methode, kombiniert Pfade mit Pfaden in der UmgebungModule._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; // ... };
Module._resolveLookupPahts
方法
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' ]
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
核心流程
\x00
合并生成cacheKey
)(\x00
是空格的16进制)Module._resolveLookupPahts
方法生成的paths
数组,将path
与request
组成文件路径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!