Node的模块加载

在Node中,模块分为两类

  • Node提供的内置模块,称之为核心模块
  • 用户编写的自定义模块,称之为文件模块

而如果在Node中引入模块,主要经历三个步骤

  1. 路径分析
  2. 文件定位
  3. 编译执行

优先缓存加载

在Node中,如果是第二次通过require方法对模块进行引入,那么会采取优先缓存的方式。Node会将第一次编译和执行的对象放入缓存,提升效率。
而对于核心模块和文件模块,核心模块的检查优先于文件模块,例如你自己定义一个http,那么肯定是找不到滴。

路径分析

因为在Node中,是通过require来对模块进行引入的。而针对于不同的模块,查找和定位有不同程度上的差异。

(1)核心模块:主要是fs,http等模块。

核心模块的优先级仅次于缓存加载,它在Node的源代码编译中,加载过程最快。

(2)路径形式的文件模块:类似于require('./index.css')

这种文件模块,会被Node将路径转换为真实路径,用真实路径作为索引,将编译的结果存放到缓存中,一遍二次加载的时候速度更快。

(3)自定义模块:类似于require('mongose')

这种模块也一般都是通过npm进行下载安装的,它的查找是最费时的。而它的查找规则和JS中的原型链很像,不过不是__propto__,是通过node_modules一层层向上查找,直到找到目标文件为止。

文件定位

(1)文件扩展名分析

CommonJS允许在标识符中不存在文件扩展名,这个时候,Node会依次按.js,.json,.node给它拼上,然后在定位,尝试是否能给它找到。
由于Node是单线程,所以这个过程可能会引起性能问题

(2)目录分析包
有的时候,require引入的可能是一个文件夹,是一个目录。这个时候,Node会在这个文件夹中找到package.json文件,通过JSON.parse()解析出描述对象,拿到main属性指定的文件名进行定位。
如果没有找到呢,就会默认去找目录下的index,再一次按照.js,.json,.node进行查找。
如果这都没找到,那就G,抛出查找失败的异常。

模块编译

在Node中,每一个文件模块都是一个对象,它们的定义如下:

function Modal(id,parent){
  this.id = id;
  this.exports = {};
  this.parent = parent;
  if(parent && parent.children){
    parent.children.push(this)
  }
  this.filename = null;
  this.loaded = false;
  this.children = []
}

对于不同的文件类型:

  • .js文件:通过fs模块,同步读取文件后执行编译。
  • .node文件:通过C++的dlopen()加载执行
  • .json文件:通过JSON.parse()解析返回结果
  • 其余扩展名:都被当做.js文件载入

require,exports,module这三个东西在哪放着

不仅是这三个,还有__filename,__dirname。他们都是从何而来的呢?

事实上,在编译的过程中,Node会对JS文件的内容进行包装一下,在头部加上一个(function (exports,require,module,__filename,__dirname) {\n,在后面加上一个\n})

(function (exports,require,module,__filename,__dirname) {
  const math = require('math')
})()

给每个模块生成对应的方法,然后再将当前模块对象的这些属性和方法传进去。所以这些变量没有定义,但也能使用。

你可能感兴趣的:(node.js)