引题

用过node的同学应该都知道require是用来加载模块的,那你是否存在如下的疑问呢?

1. require(path)是如何依据path找到对应module呢?

2. 为何在模块定义中,一定要通过module.exports暴漏出接口?module.exports与require存在什么关系

对上述问题进行概括可以抽象出如下两个问题:

1. module的路径分析
2. 文件加载

切入

   首先来直观地看看require是什么? 

// node环境下执行:
console.log(require.toString) //输入结果为:
'function require(path) {\n return self.require(path);\n }'

  上述代码说明require函数仅仅是module.require的封装,这样就需要查看node中的module源代码了。

加载模块的方式

首先来直观来认识一下node的模块加载方式有哪些方式:

case 1:

// 'path'为node的核心模块
var path = require('path')

case2:

// a.js,路径为: basePath/a.js
var myModule = require('./my-module')
// my-module的路径为basePath/node_modules/myModule.js

case 3:

// a.js, 路径: basePath/a.js
var main = require('./')
// basePath下还包括package.json, index.js

路径解析

在node的官方API中,我们可以找到这段描述:

To get the exact filename that will be loaded when require() is called, use the require.resolve() function.

Putting together all of the above, here is the high-level algorithm in pseudocode of what require.resolve does:

......

试试在node环境下用用require.resolve这个API:

require.resolve('./a.js')
// 这样就得到a.js的绝对路径

为了探索缘由,就从node核心代码中的mdoule.js找答案吧:

require.resolve = function(request) {
  return Module._resolveFilename(request, self);
} Module._resolveFilename = function(request, parent) {
  // 判断是否为node的核心模块
  if (NativeModule.exists(request)) {
  return request;
 }
// 得到查询路径,格式为数组:[id, [paths]]
var resolvedModule = Module._resolveLookupPaths(request, parent); var paths = resolvedModule[1];
  // 根据path、fileName得到绝对路径
  var filename = Module._findPath(request, paths);
return filename;
}

那Module._resolveLookupPaths是如何得到所有查询路径的呢?

  1. 为node的核心模块,stop
  2. 以./或../开头,本地查找, stop
  3. 沿着文件树,得到node_module的所有路径,直到/node_modules,在node_module中查找,stop
  4. path为目录,则检查package.json文件是否存在main属性,否则默认为index.js
  5. 最后返回new Error('Cannot find module"' + request + '"');

模块加载

先看require的源代码:

// 我们经常使用的require函数
function require(path) {
return self.require(path);
}
// 调用_load函数,加载所需的模块
Module.prototype.require = function(path) {
  return Module._load(path, this);
}

这样模块函数的调用连接到了Module._load函数:

Module.cache = {};
Module._load = function() {
  // 检测模块是否已经加载过
  var cachedModule = Module._cache[filename];
if (cachedModule) {
  return cachedModule.exports;
}
// 模块还未加载,则为模块创建module实例
var module = new Module(filename, parent);
  // 新创建的实例存储于cache中
 Module._cache[filename] = module;
// 开始获取模块的内容
module.load(filename);
// 对外提供接口
return module.exports;
}

  接下来问题的关键就变成了module.load,该方法用于获取module的内容,然后进行解析:

Module.prototype.load = function(filename) {
  // 解析出文件的后缀, 存在['.js', '.json', 'node']三种后缀
  var extension = path.extname(filename) || '.js';
  // 根据后缀,获取相关的模块
  Module._extensions[extension](this, filename);
}

  node会匹配按照.js、.json、.node三种格式进行模块匹配,根据文件类型的不同采取不同的加载策略,但是以实际开发中以加载.js最多,该种策略最后需要调用Module.prototype._compile进行编译处理:

Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
}; Module.prototype._compile = function(content, filename) {
  //将内容放入到(function() { content }),形成闭包,创建私有作用域
  var wrapper = Module.wrap(content);
  // bind新的执行上下文
  var compiledWrapper = runInThisContext(wrapper, { filename: filename });
  // 向外暴漏接口:module.exports, require, module,__filename, __dirname,
  var args = [self.exports, require, self, filename, dirname];
  return compiledWrapper.apply(self.exports, args);
}

  这样,我们就可以在require来获取相应地module。

结论
      node现在这么火,各种优势铺天盖地涌来,会让刚刚入行的人觉得深不可测,因而往往会让人望而却步。但是只要我们敢于突破第一步,深入下来仔细分析,就会发现其实没有那么晦涩难懂,踏出第一步真的很关键!

参考资料

http://thenodeway.io/posts/get-fancy/how-require-actually-works/

https://github.com/joyent/node/blob/master/lib/module.js

http://nodejs.org/api/modules.html

https://github.com/substack/node-resolve

你知道require是什么吗?的更多相关文章

  1. WCF : 修复 Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service 问题

    摘要 : 最近遇到了一个奇怪的 WCF 安全配置问题, WCF Service 上面配置了Windows Authentication. IIS上也启用了 Windows Authentication ...

  2. webpack解惑:require的五种用法

    我之前在 <前端搭环境之从入门到放弃>这篇文章中吐槽过,webpack中可以写commonjs格式的require同步语法,可以写AMD格式的require回调语法,还有一个require ...

  3. express全局安装后无法通过require使用

    今天入门了一下express,首先安装依赖. npm install express -g; npm install body-parser -g; npm install cookie-parser ...

  4. require() 源码解读

    2009年,Node.js 项目诞生,所有模块一律为 CommonJS 格式. 时至今日,Node.js 的模块仓库 npmjs.com ,已经存放了15万个模块,其中绝大部分都是 CommonJS ...

  5. 项目开发(Require + E.js)

    最近在做的几个项目,分别用了不同的框架跟方式,有个H5的项目,用了vue框架, 这个项目我还没有正式加入进去, 等手头的这个项目完成就可以去搞vue了, 现在手头的这个项目是一个招聘的项目, 用到了N ...

  6. Javascript模块化编程(三):require.js的用法

    Javascript模块化编程(三):require.js的用法 原文地址:http://www.ruanyifeng.com/blog/2012/11/require_js.html 作者: 阮一峰 ...

  7. Torch Problems: require some packages doesn't work

    I've recently got a problem. require 'cutorch' doesn't work. But it was ok yesterday, although I hav ...

  8. javascript模块化编程(三):require.js用法

    本文来自阮一峰 这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战. 我采用的是一个非常流行的库require.js. 一.为什么要用require ...

  9. PHP中include()与require()的区别说明

    require 的使用方法如 require("MyRequireFile.php"); .这个函数通常放在 PHP 程序的最前面,PHP 程序在执行前,就会先读入 require ...

  10. CCLuaLoadChunksFromZIP加载后的require路径问题

    对于require来说,在LUA中的机制就是搜索path路径了.但对于CCLuaLoadChunksFromZIP加载的LUA文件来说,require的路径又是怎么样的呢? 我在服务器上有一个 oox ...

随机推荐

  1. C#基础之yield与Singleton

    1.实例解析yiled的作用 最近参加java笔试题第一次见到yield这个关键字,既然遇见了那肯定要掌握,下面是C#中关于yield关键字的总结.yield这个关键字作用于迭代器块中,其最本质的功能 ...

  2. 几种Boost算法的比较(Discrete AdaBoost, Real AdaBoost, LogitBoost, Gentle Adaboost)

    关于boost算法 boost算法是基于PAC学习理论(probably approximately correct)而建立的一套集成学习算法(ensemble learning).其根本思想在于通过 ...

  3. windows API 开发飞机订票系统 图形化界面 (三)

    来吧,接下来是各个功能的函数的实现代码. 首先,程序运行时加载读入账户信息和航班信息.接下来就该读取文件了. 我把账户资料和航班信息储存在了.txt文件里 那么问题就来了,挖掘机...额,不对,应该怎 ...

  4. hello,world!

    这可以算是我的第一篇博客了吧?真正意义上的开始... 我于2016年6月毕业于山东英才学院信工学院计算机管理系,至今已有近半年.几经周转,总算是准备稳定下来了.于是,当初的博客想法提上了日程. 本人工 ...

  5. “耐撕”2016.04.13站立会议

    1. 时间 : 19:40--20:00  共计20分钟 2. 人员 : Z   郑蕊 * 组长 (博客:http://www.cnblogs.com/zhengrui0452/), P 濮成林(博客 ...

  6. Daily Scrum – 1/12

    Meeting Minutes Merge Wordlist & Word Recite entry. (P0) – Done. Remove "Word Challenge&quo ...

  7. android学习——error opening trace file: No such file or directory (2)

    1.疑惑: 程序运行起来的时候日志总是显示下面这个错误,但是不影响程序的正常进行,我是用真机来测试的,android4.4.4(API17). 02-11 14:55:03.629 15525-155 ...

  8. 从topcoder赚钱的方法

    1. 算法1.1  SRM 钱少($30左右),而且很难.1.2  Tournament 钱多($1000~$10000),太难~ 2. 设计和开发2.1  构件设计和开发 钱比较多($1000左右) ...

  9. jQuery Select的操作集合

    1. $("#select_id").change(function(){... });   //为select添加事件,当选择其中一项时触发2. $("#select_ ...

  10. Windows下python的配置

    Windows下python的配置 希望这是最后一次写关于python的配置博客了,已经被python的安装烦的不行了.一开始我希望安装python.手动配置pip并使用pip安装numpy,然而发现 ...