node的路徑解析require.resolve

路徑解析 require.resolve

採用模塊化方式編寫代碼讓咱們能夠更好的組織代碼結構,node在解析依賴時,首先會獲取依賴的文件是否存在,即每當遇到importrequire這些導入語法時,就會根據規則去找到須要解析的文件路徑。node

使用方法

在node中,可使用require.resolve來查詢某個模塊的完整路徑,使用方式以下:json

// 絕對路徑 -> /Users/enhanced-resolve/lib/node.js
require.resolve('/Users/enhanced-resolve/')
// 相對路徑 -> /Users/enhanced-resolve/index.js
require.resolve('./index')
// 模塊路徑 -> /Users/enhanced-resolve/node_modules/diff/diff.js
require.resolve('diff')
複製代碼

剛開始看到結果可能會疑惑,爲何返回結果是node.js?他是怎麼找到node_modules下的diff.js?下面咱們進入node的源碼中看看它的執行原理。緩存

執行原理

require是node在解析js時向每一個文件注入的對象,最多見用法就是使用CommonJs語法引入其餘js,另外node還向這個函數添加了一寫功能方法,其中一個路徑解析resolve就是咱們如今要研究的方法:模塊化

// node定義的函數
function resolve(request, options) {
    validateString(request, 'request');
    return Module._resolveFilename(request, mod, false, options);
}
複製代碼

在下面分析中咱們只看主流程,會忽略掉一些次要功能如參數校驗,緩存,和軟鏈接等的處理函數

Module._resolveFilename

Module._resolveFilename(request, parent, isMain, options) {
    var paths;
    if(options.paths) {
        // ...
    } else {
        paths = Module._resolveLookupPaths(request, parent, true)
    }
    var filename = Module._findPath(request, paths, isMain);
    return filename
}
複製代碼

Module._resolveLookupPaths

用於獲取有可選的路徑,以解析文件"/Users/enhanced-resolve/index.js"爲例:ui

參數request的幾種常見狀況:spa

  • 爲絕對路徑時,其實返回什麼都不要緊,由於下一步執行Module._findPath時會忽略這裏的返回值。
  • .時,會把當前解析文件目錄放在第一位置,後續解析先在當前文件夾下查找。
  • 爲相對路徑時,僅返回解析文件所在目錄。
  • 爲模塊路徑時,返回parent.paths,後續在這些模塊文件夾裏查找。

parent.paths裏存了調用文件的目錄及它全部上級目錄下的node_modules時,將會有如下三個值:code

  • "/Users/enhanced-resolve/node_modules"
  • "/Users/node_modules"
  • "/node_modules"
Module._resolveLookupPaths = function(request, parent) {
    // [ '/path/to/file', '.', 'diff' ]
    if (request.length < 2 || request[0] !== '.' ||
      (request[1] !== '.' && request[1] !== '/')) {
        paths = parent.paths
        if (request === '.' && parent.filename) {
            paths.unshift(path.dirname(parent.filename));
        }
        return paths
    }

    return [path.dirname(parent.filename)]
}
複製代碼

Module._findPath

若是是絕對路徑則分析該路徑下是否可找到文件,若是是相對路徑則分析是否在給定的文件夾下對象

Module._findPath = function(request, paths) {
    if (path.isAbsolute(request)) {
        paths = [''];
    }
    for (var i = 0; i < paths.length; i++) {
        var basePath = path.resolve(paths[i], request);
        // .js .json .node
        var exts = Object.keys(Module._extensions);
        var filename;
        var rc = stat(basePath);
        if (rc === 'File') {
            filename = basePath
        }
        if (!filename) {
            filename = tryExtensions(basePath, exts);
        }
        if (!filename && rc === 'Directory') {
            filename = tryPackage(basePath, exts);
            if (!filename) {
                filename = tryExtensions(path.resolve(basePath, 'index'), exts);
            }
        }
        if(filename) return filename
    }
}
複製代碼

Module.tryPackage

判斷該路徑是不是一個node package,若是是就根據描述文件的main字段來查找文件源碼

function tryPackage(requestPath, exts) {
    const jsonPath = path.resolve(requestPath, 'package.json');
    const json = internalModuleReadJSON(jsonPath);
    if (json === undefined) return false;
    const name = JSON.parse(json).main

    var filename = path.resolve(requestPath, name);
    return tryFile(filename) ||
        tryExtensions(filename, exts) ||
        tryExtensions(path.resolve(filename, 'index'), exts);
}

複製代碼

Module.tryExtensions

判斷加上擴展名後的路徑是不是文件

function tryExtensions(p, exts) {
  for (var i = 0; i < exts.length; i++) {
    const filename = tryFile(p + exts[i]);
    if (filename) return filename;
  }
  return false;
}
複製代碼

Module.tryFile

判斷路徑是不是文件

function tryFile(requestPath) {
  const rc = stat(requestPath);
  return rc === 0 && toRealPath(requestPath);
}
複製代碼

實戰演練

從上面的代碼來看,總體流程仍是很是清晰的,如今再來分析最開始的幾個疑惑:

絕對路徑 resolve('/Users/enhanced-resolve/')

  • 進入_findPath,由於是絕對路徑,因此僅在該目錄下查找;
  • 進入tryPackage,獲取到該目錄下描述文件package.json,其中main: ./lib/node.js,使用該路徑查找;
  • 進入tryFile,解析出/Users/enhanced-resolve/lib/node.js

相對路徑 resolve('./index')

  • 進入_resolveLookupPaths,由於是相對路徑,只返回一個搜索目錄[/Users/enhanced-resolve]
  • 進入_findPath,由於/Users/enhanced-resolve/index不是文件,因此嘗試使用擴展名查找
  • 進入tryExtensions,解析出/Users/enhanced-resolve/index.js;

模塊路徑 resolve('diff')

  • 進入_resolveLookupPaths,返回多個搜索目錄["/Users/enhanced-resolve/node_modules", "/Users/node_modules", "/node_modules"];
  • 進入_findPath,首先解析第一個可能的目錄/Users/enhanced-resolve/node_modules;
  • 進入tryPackage,獲取到該目錄下描述文件package.json,其中main: ./diff,使用該路徑查找;
  • 進入tryFile發現/Users/enhanced-resolve/node_mdoules/diff/diff不是文件,返回;
  • 進入tryExtensions,解析出/Users/enhanced-resolve/node_mdoules/diff/diff.js;
相關文章
相關標籤/搜索