首页前端开发JavaScript一文聊聊Node.js中的模块路径解析

一文聊聊Node.js中的模块路径解析

时间2024-01-31 02:12:03发布访客分类JavaScript浏览1047
导读:收集整理的这篇文章主要介绍了一文聊聊Node.js中的模块路径解析,觉得挺不错的,现在分享给大家,也给大家做个参考。本篇文章带大家了解一下Node.js中的模块路径解析,介绍一下Node模块路径解析方法,希望对大家有所帮助!require案...
收集整理的这篇文章主要介绍了一文聊聊Node.js中的模块路径解析,觉得挺不错的,现在分享给大家,也给大家做个参考。本篇文章带大家了解一下Node.js中的模块路径解析,介绍一下Node模块路径解析方法,希望对大家有所帮助!

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.cjsconsole.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']*/

require解析并找到模块执行文件的流程

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数组,将pathrequest组成文件路径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使用到内置模块的方法

当我们使用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数组,将pathrequest组成文件路径basePath
  • 如果basePath存在则调用fs.realPahtSync获取文件的真实路径

fs.realPahtSync

更多node相关知识,请访问:nodejs 教程!!

以上就是一文聊聊Node.js中的模块路径解析的详细内容,更多请关注其它相关文章!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

上一篇: 了解TypeScript数据类型中的模板...下一篇:一分钟彻底理解JavaScript冒泡排...猜你在找的JavaScript相关文章 html font标签如何设置字体大小?html font标签属性用法介绍2022-05-16vue3+TypeScript+vue-router的使用方法2022-04-16vue3获取当前路由地址2022-04-16如何利用React实现图片识别App2022-04-16JavaScript展开运算符和剩余运算符的区别详解2022-04-16微信小程序中使用vant框架的具体步骤2022-04-16Vue elementUI表单嵌套表格并对每行进行校验详解2022-04-16如何利用Typescript封装本地存储2022-04-16微信小程序中wxs文件的一些妙用分享2022-04-16JavaScript的Set数据结构详解2022-04-16 其他相关热搜词更多phpjavapython程序员loadpost-format-gallery

若转载请注明出处: 一文聊聊Node.js中的模块路径解析
本文地址: https://pptw.com/jishu/593344.html
ASP是什么 jQuery怎样实现简单的动画效果?(实例详解)

游客 回复需填写必要信息