• 技术文章 >web前端 >js教程

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

    青灯夜游青灯夜游2021-12-16 19:19:31转载502
    本篇文章带大家了解一下Node.js中的模块路径解析,介绍一下Node模块路径解析方法,希望对大家有所帮助!

    require案例

    1.png

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

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

    1、Nodejs项目模块路径解析是通过require.resolve方式实现的。

    2、Module._findPath核心流程是:

    3、fs.realPahtSync核心流程:

    4、require.resolve.paths等价于Module._resolveLookupPaths,该方法获取所有node_modules可能存在的路径组成一个数组。

    5、require.resolve.paths实现原理是:

    require使用到内置模块的方法

    当我们使用require('yargs')

    require方法

    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方法

    2.png

    // 进入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循环的核心算法解析:

    3.png

    Module._load方法

    Module._load(id, this, /* isMain */ false)

    核心实现代码是:const filename = Module._resolveFilename(request, parent, isMain);

    require.resolve

    Node.js项目模块路径解析是通过require.resolve方式实现的。

    // 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核心流程

    return Module._resolveFilename(request, parent, isMain);

    4.png

    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方法

    生成

    [
      '/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'
    ]

    5.png

    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核心流程

    6.png

    fs.realPahtSync

    7.png

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

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

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    上一篇:一分钟彻底理解JavaScript冒泡排序与选择排序 下一篇:了解TypeScript数据类型中的模板字面量
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• ubuntu怎么安装node指定版本• node.js是什么?能做些什么?• node fs模块怎么检测文件是否存在• node原生模块有哪些• 聊聊Nodejs-cluster模块,介绍一下其使用方法• 详解node中进程通信的几种实现方式
    1/1

    PHP中文网