您好,登錄后才能下訂單哦!
這篇文章主要介紹“Node.js中的模塊路徑是怎樣的”,在日常操作中,相信很多人在Node.js中的模塊路徑是怎樣的問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Node.js中的模塊路徑是怎樣的”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
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' ] */
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('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);
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
方法
生成要查找模塊的所有路徑上可能存在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' ]
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
核心流程
查詢緩存(將request和paths通過\x00
合并生成cacheKey
)(\x00
是空格的16進制)
遍歷Module._resolveLookupPahts
方法生成的paths
數組,將path
與request
組成文件路徑basePath
如果basePath存在則調用fs.realPahtSync
獲取文件的真實路徑
fs.realPahtSync
到此,關于“Node.js中的模塊路徑是怎樣的”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。