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;
相关文章
相关标签/搜索