美文网首页
Node.js模块:require实现原理、 exports与m

Node.js模块:require实现原理、 exports与m

作者: 苦苦修行 | 来源:发表于2018-07-28 23:38 被阅读0次

参考文献: module-模块(来自官方文档)

1. exportsmodule.exports的区别

分析一下node.js中require方法的实现原理,一切就都明白了!(所以可以跳过第一步,直接看require实现原理)

我们先将需要到的元素准备好,假设我们有两个文件:
一个是我们需要导入的模块文件,比如起名叫my-module-file.js
另一个是需要导入my-module-file.js的文件,比如起名叫index.js

这两个文件的代码比如如下所示:

/**
* my-module-file.js
*/
var one = 1;
var two = 2;
exports.a = one;
module.exports.b = two;
/**
* index.js
*/
var MyModule = require('./my-module-file.js'); // 其实 return module.exports

/*
* 输出为1
* why?上面不刚说了 require 是 return module.exports吗?
* 按道理讲不应该输出 undefined 吗?module.exports 中不是没有属性 a 吗?
* 这是因为在node.js的模块系统中,初始状态是 exports = module.exports = {}
* 所以他们共同指向同一个对象,肯定有属性 a
*/
console.log(MyModule.a);

console.log(MyModule.b); // 输出为2

改下一下再看看👀效果

/**
* my-module-file.js
*/
var one = 1;
var two = 2;
exports = { a: one };
module.exports.b = two;
/**
* index.js
*/
var MyModule = require('./my-module-file.js'); // 其实 return module.exports

/*
* 输出为 undefined
* why?怎么不是输出为 1 了?
* 这是因为 exports = module.exports = {} 这个初始状态被 exports = { a: one } 这个赋值破坏了
* 这时他们已经不再指向同一个对象了
* exports指向的是 { a: one } 这个对象,
* 而 module.exports 还是指向初始状态的那个对象,不过因为添加了属性 b,所以这个对象的看起来是 { b: two }
*/
console.log(MyModule.a); 

console.log(MyModule.b); // 输出为2

好了,先对上面说的一大堆来个小结:

  • 本质原因:exports = module.exports = {}
  • require 方法 return 的是 module.exports
  • 为什么要设计个 exports?简化类似 module.exports.b = two这种代码,你就可以直接写成exports.b = two了。当然,前提是不要改变初始时的对象指向,比如exports = { a: one }就是改变初始时的对象指向了

2. require实现原理:

有没有想过require方法是怎样导出模块的?
require的方法实现应该看起来像是这样的:

function require(your-module){

  var module = {
    exports: {}
  };
  
  var exports = module.exports;

  (function(exports, module) {

    /**
    * 将 your-module 里你写的代码放在了这个立即执行的函数里
    * node.js是怎么将你的代码放过来的呢,应该是通过文件读取操作,将文件内容写入进来的
    * 具体文件读取操作是怎么实现的,不在本文讨论
    * 那接下来我们将 your-module 里的代码搬过来看看是什么样子的呗
    * 那我们就把 my-module-file.js 里的代码搬过来呗
    */
    var one = 1; // 知道为啥在模块文件(my-module-file.js)中定义的变量仅限在模块文件中访问了吧
    var two = 2; // 因为他们都是在“立即执行函数”里执行的,外面肯定访问不到
    exports.a = one;
    module.exports.b = two;

  })(exports, module);

  return module.exports;
}

看完上面这段代码,是不是就很清晰了。


3. 模块查找机制

require(your-module)

  • 如果your-module是以./ 或 ../开头的,那就以当前文件所在目录的相对地址去查找模块的,以/开头就是以根目录去查找模块呗(好像没见用过/

  • 如果不是以上面的这些情况开头的,就是去查找node相关的模块了,怎么说?
    ① 首先是查找node.js核心模块,啥叫核心模块?就是编译进node命令里面的模块,比如http模块
    ② 第二步就是查找node_modules里面的模块了, 首先会先去缓存里查找模块。那node是怎么匹配缓存里的模块的呢?它是根据你 当前请求的模块的实际路径 缓存里存储的模块所对应的路径 是否一致(字母大小写也必须完全匹配) 来判断缓存中是否有此模块,有,则从缓存中加载,无,则从node_modules加载
    ③ 那怎么从node_modules加载呢?node会以当前文件所在目录一层层的往上寻找node_modules,直到找到node_modules
    ④ 接下来,一般从node_modules中会寻找your-module.js 或 your-module.node 或 your-module/index.js 或 your-module/package.json这几个文件(注意:这个your-module有可能是@angular/core这种形式,也有可能是ionic-angular这种形式)
    ⑤ 查找到your-module/package.json这个文件后是怎么实现模块加载的呢?这个package.json文件里会配置main属性,对应一个js文件,比如main: 'lib/ajv.js',那么node加载的就是your-module/lib/ajv.js这个文件了(注意:可能是${node_modules}/@angular/core/lib/ajv.js这种形式,也有可能是${node_modules}/ionic-angular/lib/ajv.js这种形式,${node_modules}代表③中找到的node_modules的实际路径)


题外话:

require.resolve(request[, options])这个方法会解析你请求的模块,返回给你这个模块文件的实际路径

相关文章

  • Node.js模块:require实现原理、 exports与m

    参考文献: module-模块(来自官方文档) 1. exports与module.exports的区别 分析一下...

  • Node.js

    模块Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的接口,r...

  • xdh Day6 20160912 笔记

    xdh Day6 20160912 笔记 Node.js的模块 require exports Node.js文...

  • 模块

    一个Node.js文件就是一个模块 Node.js提供exports和require两个对象,其中 exports...

  • 模块化

    1、exports与require 模块导出 模块导入

  • Nodejs

    nodejs的模块 require():导入一个模块 exports():导出一个模块 exports()与mod...

  • javascript模块化总结

    CommonJS 规范 实现js模块化模块加载 require模块标识 module模块定义 exports No...

  • 模块化

    在nodejs中,可以通过exports或module.exports 和 require 实现模块化export...

  • node的模块加载

    模块实现 依照CommonJS的模块规范,使用require()引入模块的api,使用exports导出模块的方法...

  • require/module/exports

    require(string path) 引入模块。返回模块用过module.exports 和exports暴露...

网友评论

      本文标题:Node.js模块:require实现原理、 exports与m

      本文链接:https://www.haomeiwen.com/subject/addamftx.html