一、模块规范
NodeJS
对CommonJS
进行了支持和实现,让我们在开发node
的过程中可以方便地进行模块化开发:
- 在 Node 中每一个 js 文件都是一个单独的模块
- 模块中包括 CommonJS 规范的核心变量:exports、module.exports、require
- 通过上述变量进行模块化开发
模块化的核心是导出和导入,在Node
中通过exports
与module.exports
负责对模块中的内容进行导出,通过require
函数导入其他模块(自定义模块、系统模块、第三方库模块)中的内容
二、查找策略
require方法接收一下几种参数的传递:
- 原生模块:http、fs、path等
- 相对路径的文件模块:./mod或../mod
- 绝对路径的文件模块:/pathtomodule/mod
- 目录作为模块:./dirname
- 非原生模块的文件模块:mod
require参数较为简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同,如下图:
从上图可以看见,文件模块存在缓冲区,寻找模块路径的时候都会优先从缓存中加载已经存在的模块。
原生模块
像原生模块这些,通过require
方法在解析文件名之后,优先检查模块是否在原生模块列表中,如果在则从原生模块中加载。
绝对路径、相对路径
如果require
绝对路径的文件,则直接查找对应的路径,速度最快;
相对路径的模块则相对于当前调用require
的文件去查找,如果按确切的文件名没有找到模块,则NodeJS
会尝试带上.js
、.json
等拓展名再加载
目录作为模块
默认情况是根据根目录中package.json
文件的main
来指定目录模块,如:
{
"name" : "some-library",
"main" : "main.js"
}
如果这是在./some-library node_modules
目录中,则require('./some-library')
会试图加载 ./some-library/main.js
如果目录里没有 package.json
文件,或者 main
入口不存在或无法解析,则会试图加载目录下的 index.js
或 index.node
文件
非原生模块
在每个文件中都存在module.paths
,表示模块的搜索路径,require
就是根据其来寻找文件
在window
下输出如下:
[ 'c:\\nodejs\\node_modules',
'c:\\node_modules' ]
可以看出module.path
的生成规则为:从当前文件目录开始查找node_modules
目录:然后依次进入父目录,查找父目录下的node_modules
目录,依次迭代,直到根目录下的node_modules
目录
当都找不到的时候,则会从系统NODE_PATH
环境变量中查找
举个栗子:
如果在/home/ry/projects/foo.js文件里调用了 require(‘bar.js’),则 Node.js 会按以下顺序查找:
- /home/ry/projects/node_modules/bar.js
- /home/ry/node_modules/bar.js
- /home/node_modules/bar.js
- /node_modules/bar.js
三、总结
通过上面模块的文件查找策略后,总结下文件查找的优先级:
- 缓存的模块优先级最高
- 如果是内存模块,则直接返回,优先级仅此缓存的模块
- 如果是绝对路径 / 开头,则从根目录寻找
- 如果是相对路径 ./ 开头,则从当前 require 文件相对位置寻找
- 如果文件没有携带后缀,先从 js、json、node按顺序查找
- 如果是目录,则根据
package.json
的main
属性值决定目录下入口文件,默认情况为index,js
- 如果文件为第三方模块,则会引入
node.modules
文件,如果不再当前仓库文件中,则自动从上级递归查找,直到根目录