nodeJS模块查找策略

es6和nodeJS模块查找策略主要分这么三步:

  • 路径分析【找到指定文件所在的目录在哪儿】
  • 文件定位【在找到文件的目录后,找到指定的那个文件【不是文件夹】】
  • 编译执行

其中我们需要重点分析的是前面两步。

由于不同类型的模块的路径分析、文件定位的策略是不同的,所以我们先探讨一下我们一般将模块分为哪几类。

模块分类

nodeJS中的模块主要分为两大类。

  • 一类是nodeJS内置的模块,称为核心模块,如http、ftp、path等模块【npm包可不是核心模块,虽然这两者的引用代码格式是一样的】【核心模块定义在 Node.js 源代码的 lib/ 目录下。】。
  • 另一类是用户编写的模块,称为文件模块【npm包与自己写的模块文件都是文件模块】。

他们的关系如下图所示:

模块引入的代码格式

  • 核心模块:

var http = require('http');

  • 路径形式的模块:

var moduleA = require('./moduleA.js');

切记,路径形式的模块写路径时,最开头一定要有./或../或/,否则的话会被认为是核心模块或自定义模块。

  • 自定义模块:

var express = require('express');

虽说书写形式和核心模块一样,但他们两最大的差别就在于自定义模块在nodeJS中不是内置就有的,需要从npm库中下载下来,才能被模块引入。而核心模块是内置就有的,不需要下载就能模块引入【不过就算是核心模块,也得require引入了才能够被使用】

在这里给大家看一张图:

也就是说nodeJS并不是完全等于ECMAScript,还得有许多内置的模块如FS、TCP、http等,才能被称作是nodeJS。

同理浏览器中的JS也是,浏览器中的JS并不完全等于ECMAScript【构成了JS核心语法基础】,还得有内置模块BOM【浏览器对象模型,用来操作浏览器上的对象】、DOM【文档对象模型,用来操作网页中的元素】。

路径分析

  • 核心模块:

由于核心模块是在nodeJS编译时就被加载进来的,所以核心模块不需要路径分析和文件定位。

  • 路径形式的模块:

由于指定了确切的路径,引入时require方法会把路径转化为硬盘上的真实路径,并用这个路径作为索引将编译后的结果进行缓存【索引:文件名,值:模块对象】。

  • 自定义模块:

自定义模块的路径分析要比路径形式引入的模块要复杂一些,主要是因为他的文件路径不像后者是定死的,可能是当前目录下的node_modules目录下的对应文件夹名,也可能是父,甚至以上。

同样的,寻到的模块文件也会被存入缓存中。

文件定位

当完成路径分析后,nodeJS会进行文件定位。

核心模块是不需要文件定位这一步骤的,而路径形式的模块和自定义模块的文件定位模式是一致的。

由于CommonJS规范和es6模块化都允许文件的标识符不带扩展名甚至说不用指定最终的文件名,直接写到上一层目录名就可以,针对这两种情况有不同的处理方式。

  • 正常的书写方式【只有路径形式模块会这样写】

    • require('./a.js');,当路径分析成功后,就直接找寻该目录下是否有同名文件,有则命中,无则报错
  • 省略扩展名的声明方式【也只有路径形式模块会这样写】

    • 会在该目录下按照.js、.node、.json的顺序逐一尝试。
    • 由于该声明形式和第三种声明形式很像,所以策略是会先以省略扩展名的声明形式来文件定位,如果没命中,则以美元指定最终的文件名的声明形式来文件定位,再没命中,才会报错。
  • 没有指定最终的文件名,直接写到上一层目录名【路径形式模块和自定义模块都会这样写】

    1. 首先会在命中的目录下寻找package.json这个文件,并取出json文件中的main属性的值,作为命中的文件
    2. 如果找不到package.json或对应的main属性,则会用index作为文件名,而后依旧按照.js、.node、.json顺序去尝试【如果依旧找不到对应的文件,如果是路径形式的模块则定位文件失败了,如果是自定义模块会往上一层目录下的node_modules继续找,直至命中或再无上一层】
require() 总是会优先加载核心模块。 例如, require('http') 始终返回内置的 HTTP 模块,即使有同名文件。也就是核心模块和自定义模块总是先假设这个路径是核心模块去找一遍,如果未命中则就是自定义模块了。

nodeJS对模块加载做了一定程度的优化,所有文件只要被引用过一次,就会被缓存起来。下一次引用时,会先检查缓存中有没有对应的文件,即优先从缓存中加载文件,使得速度更快。

你可能感兴趣的