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 !
require
Case/Utilisateurs/ 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' ]
requireLe 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 lerequire. méthode de résolution
. 🎜Module._resolveFileName
Module._resolveFileName
Le processus principal est : 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 cheminModule._findPath</ code>, < /li></ul></li></ul>🎜2. <code>Module._findPath
Le processus principal est : 🎜 x00
Fusionner pour générer cacheKey
)Module._resolveLookupPahts
et comparez path
avec request
compose le chemin du fichier basePathbasePath
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)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 : 🎜/
(chemin racine), renvoyez directement ['/node_modules ']
require(' yargs')
🎜 🎜🎜méthode require🎜Module. Méthode _load
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._nodeModulePaths
méthode🎜🎜🎜rrreee🎜Analyse de l'algorithme de base de la boucle for :🎜🎜🎜🎜🎜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
. 🎜Module._resolveFileName
, Module._resolveFileName
Core Process🎜Méthode Module. _resolveLookupPahts
, combine les chemins avec les chemins dans l'environnementModule._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 教程!!
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!