博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
require源码阅读
阅读量:7104 次
发布时间:2019-06-28

本文共 5173 字,大约阅读时间需要 17 分钟。

mudule对象化

require最终会把每个模块都转化为对象

function Module(id, parent) {    this.id = id;    this.exports = {};    this.parent = parent;    updateChildren(parent, this, false);    this.filename = null;    this.loaded = false;    this.children = [];}

require方法

用assert断言输入的合法性并调用_load方法还有一个调用_load的是

Module.runMain = function() {    // Load the main module--the command line argument.    Module._load(process.argv[1], null, true);    // Handle any nextTicks added in the first tick of the program    process._tickCallback();};

这个我不是特别确定,但基本确定是给node xxx.js 这条命令调用的

load方法

这里有很多是关于处理main的,核心的一段是

if (isMain) {    process.mainModule = module;    module.id = '.';}

这个佐证了上面runMain是给node xxx.js 这条命令调用的这个论点,另外main模块的id一定是 '.',并且parent一定是空。

另外在调用前会先查找模块缓存的是否存在。
以下代码为了简化删去main模块的处理

//创建没有原型的空对象Module._cache = Object.create(null);Module._pathCache = Object.create(null);Module._load = function(request, parent, isMain) {  if (parent) {    debug('Module._load REQUEST %s parent: %s', request, parent.id);  }  var filename = Module._resolveFilename(request, parent, isMain);//缓存中是否存在  var cachedModule = Module._cache[filename];  if (cachedModule) {    updateChildren(parent, cachedModule, true);    return cachedModule.exports;  }//是否是native模块  if (NativeModule.nonInternalExists(filename)) {    debug('load native module %s', request);    return NativeModule.require(filename);  }//其他模块处理  var module = new Module(filename, parent);  Module._cache[filename] = module;  tryModuleLoad(module, filename);  return module.exports;};

这个部分说明了加载模块首先是判断模块名,之后是查找缓存,查找native 模块,然后是其他模块,最后的return是最为关键的,返回值永远是module的 exports

查找node_modules文件夹的规则

Module._nodeModulePaths = function(from) {    // guarantee that 'from' is absolute.    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'];    // note: this approach *only* works when the path is guaranteed    // to be absolute.  Doing a fully-edge-case-correct path.split    // that works on both Windows and Posix is non-trivial.    const paths = [];    var p = 0;    var last = from.length;    for (var i = from.length - 1; i >= 0; --i) {      const code = from.charCodeAt(i);      if (code === 47//*/*/) {        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;        }      }    }

从from开始逐层向上查找node_modules文件夹

Module._extensions

js,json,node,mjs

每个后缀的文件都有对应的打开方式
js 清除可能的BOM头后加载
json json Parse
node .node 这是C/C++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件,可以当作是一个系统调用

findPath

这部分代码比较多,只看一段注释即可

// given a module name, and a list of paths to test, returns the first
// matching file in the following precedence.
//
// require("a.<ext>")
// -> a.<ext>
//
// require("a")
// -> a
// -> a.<ext>
// -> a/index.<ext>

package.json

node会去寻找路径中的package.json文件并且会加载其中的main,并放入packagecache中,用main中指定的文件再去确认绝对路径然后加载

function readPackage(requestPath) {  const entry = packageMainCache[requestPath];  if (entry)    return entry;  const jsonPath = path.resolve(requestPath, 'package.json');  const json = internalModuleReadFile(path.toNamespacedPath(jsonPath));  if (json === undefined) {    return false;  }  try {    var pkg = packageMainCache[requestPath] = JSON.parse(json).main;  } catch (e) {    e.path = jsonPath;    e.message = 'Error parsing ' + jsonPath + ': ' + e.message;    throw e;  }  return pkg;}

compile

如何查找到的文件基本清楚了,之后就是最常用的js的compile了

js模块的外层一定会被套上

Module.wrapper = [  '(function (exports, require, module, __filename, __dirname) { ',  '\n});'];

之后涉及了断点的处理,是在vm模块中的Srcript对象中的runInThisContext

后面有一句说明了为什么node中的所有文件也可以拥有Module的各种方法和特性 require 包括那些并不是main的文件,另外所有的模块也是公用的模块缓存,利用Module require中的return Module._load(path, this, /* isMain */ false);把自己的this作为parent作为Module对象的parent

var require = internalModule.makeRequireFunction(this);
makeRequireFunction的代码

// Invoke with makeRequireFunction(module) where |module| is the Module object// to use as the context for the require() function.function makeRequireFunction(mod) {  const Module = mod.constructor;  function require(path) {    try {      exports.requireDepth += 1;      return mod.require(path);    } finally {      exports.requireDepth -= 1;    }  }  function resolve(request, options) {    return Module._resolveFilename(request, mod, false, options);  }  require.resolve = resolve;  function paths(request) {    return Module._resolveLookupPaths(request, mod, true);  }  resolve.paths = paths;  require.main = process.mainModule;  // Enable support to add extra extension types.  require.extensions = Module._extensions;  require.cache = Module._cache;  return require;}

执行compiledWrapper,对应wrapper插入的js

result = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname);
首先是compiledWrapper的this绑定在了Module自己exports上,自己的exports也作为了参数被注入,相当于隐式的赋值exports就是compiledWrapper中的exports了这个就成了对外唯一的输出了,其他的所有值都成了compiledWrapper的私有不会污染全局,require作为参数被注入,另外就是文件名 和路径的注入

从上述处理加载的过程我们可以发现只要有require node就会继续加载,另外CommonJs同步的特点也是在这体现出来的

转载地址:http://uuuhl.baihongyu.com/

你可能感兴趣的文章
VM虚拟机下载与安装
查看>>
深入理解Java内存模型(六)——final
查看>>
步步为营搭建spring+springmvc+mybatis(oracle)项目
查看>>
MDaemon退信分析:553 MI:SUM
查看>>
Windws Srv 2008内置账户
查看>>
PMP-5A小伙子的备考历程
查看>>
电脑开机按F1的解决方法
查看>>
gdbserver静态编译-调试失败的原因
查看>>
AD --- 活动目录的日常管理操作(2)
查看>>
linux安装VMware-tools步骤
查看>>
WPF 布局管理器之 StackPanel、WrapPanel、DockPanel (6)
查看>>
PostgreSQL配置参数详解和配置错误解决办法(二)
查看>>
搭建PHP环境后PHPINFO()中没有MYSQL的解决方法--笔记
查看>>
安装RPM包遇到:warning: : Header V3 DSA/SHA1 Signature, key ID e8562897: NOKEY
查看>>
简单的Python登陆认证小程序
查看>>
我的友情链接
查看>>
递归法列出目录中的所有文件
查看>>
H3C的命令
查看>>
LAMP平台搭建
查看>>
linux xrdp0.6 安装
查看>>