根据CommonJS规范,一个单独的文件(或文件夹)就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。
1、require函数
require函数使用一个参数,参数值可以带路径的文件名,也可以为模块名或路径
(1)require('http') 内置模块/核心模块/原生模块(http、fs、path等)是被编译成二进制代码
(2)require('lodash') 非原生模块,没指出./或../或/时可以从某个node_modules文件夹加载
非原生模块,从当前文件目录开始查找node_modules目录尝试加载,然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。
(3)require('./server')或 require('./server.js') “./”表示当前路径,表示使用相对路径
require("../lib/server")或require("../lib/server.js") “../”表示上一级目录,表示使用相对路径
(4)require('/home/marco/foo')或require('/home/marco/foo.js') 以 '/' 开头表示使用文件的绝对路径
其中,(1)(2)使用模块名,(3)(4)是使用路径(文件夹或文件)
node.js依赖modulepath(模块路径)来加载module,modulepath的生成规则:
对于每一个被加载的文件模块,创建这个模块对象的时候,这个模块便会有一个paths属性,其值根据当前文件的路径计算得到。从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。
除此之外还有一个全局module path,是当前node执行文件的相对目录(../../lib/node)。如果在环境变量中设置了HOME目录和NODE_PATH目录的话,整个路径还包含NODE_PATH和HOME目录下的.node_libraries与.node_modules。
[NODE_PATH,HOME/.node_modules,HOME/.node_libraries,execPath/../../lib/node]
在require的时候,如果带上module的路径,则按照该路径查找,如果没有就按照上面的node_modules文件夹向上追溯查找,如果都没有找到,则抛出异常。
require(路径):
如果 路径.js 存在, 执行加载并返回
如果 路径.json 存在, 执行加载并返回
如果 路径.node 存在, 执行加载并返回
如果 路径/package.json 存在,执行加载(package.json中main属性对应的路径)并返回
如果 路径/index.js 存在,执行加载并返回
如果 路径/index.node 存在,执行加载并返回
抛出异常。
require(模块名): 模块路径+全局module path
如果 模块名是系统模块, 执行加载并返回
如果 require(./node_modules/模块名) 能加载到模块(参考require(路径)),执行加载并返回
如果 require(../node_modules/模块名) 能加载到模块(参考require(路径)),执行加载并返回
递归执行到根路径,
如果require(/node_modules/模块名) 能加载到模块(参考require(路径)),执行加载并返回
抛出异常。
三类文件模块以后缀来区分,Node.js会根据后缀名来决定加载方法。
.js。通过fs模块同步读取js文件并编译执行。
.node。通过C/C++进行编写的Addon。通过dlopen方法进行加载。
.json。读取文件,调用JSON.parse解析加载。
require.paths 数组
2、exports对象
一个模块可以通过module.exports或exports将函数、变量等导出,以使其它JavaScript脚本通过require()函数引入并使用。
它有两种写法:exports.functionName = function() 和 module.exports = function()
(1)exports默认和module.exports指向同一个空对象。exports一开始是指向module.exports的引用,module.exports 初始值为一个空对象 {},所以 exports 初始值也是 {}。
(2)通过require得到的是module.exports中的内容,而不是exports的内容。
当我们想让模块导出的是一个对象时, exports 和 module.exports 均可使用(但exports也不能重新覆盖为一个新的对象),而当我们想导出非对象接口时,就必须也只能覆盖 module.exports。
module.exports = somethings 是对 module.exports 进行了覆盖,此时 module.exports 和 exports 的关系断裂,module.exports 指向了新的内存块,而 exports 还是指向原来的内存块,为了让 module.exports 和 exports 还是指向同一块内存或者说指向同一个 “对象”,所以我们就 exports = module.exports = somethings