Module
- Node 内部提供了一个
Module构建函数,所有模块都是Module的实例。 - 每个模块内部都有一个
module实例,代表其所在模块,它包含以下属性:id:标识符,通常是带有绝对路径的模块文件名,若为入口文件,则为.path:所在目录exports:对外输出的内容filename:所在目录及文件名loaded:是否已经加载完成children:调用的其他模块paths:require包时的查找路径
- Node 为了方便给每个模块都提供了一个
exports变量,它的值为module.exports对象的地址,所以不可以对exports重新赋值,这会让它断开与module.exports的联系,故不推荐使用exports。
require
-
require可以读取并执行一个模块,然后返回其module.exports的浅拷贝,若导出的基本类型在导出后在模块又内部更改了,不会影响导出的值。 -
单个模块文件仅会在首次加载时执行一次,之后其
module.exports则会被挂载到Module._cache上,属性名为模块文件所在绝对路径,后续再次加载该模块,会直接取出缓存的值,不再执行。 -
如果发生模块的循环加载,即 A 加载 B,B 又加载 A,则 B 将加载 A 的不完整版本。
-
require参数识别规则:- 若指定文件拓展名,则直接根据路径查找具体文件。
- 若不指定文件拓展名,如
require("./haha"),则按以下流程:- 查找
haha无后缀名文件,若找到则将其当作 JS 文件处理。 - 若未找到则依次为其添加
.js.json.node后缀查找。 - 若依旧没有,则将
haha当作目录名,找到该目录下的 package.json 文件中的main字段作为入口文件。 - 若
main错误或package.json不存在则尝试读取对应目录下的index.js/json/node文件。
- 查找
- 若参数为非路径形式(如
require("haha")),则按以下流程:- 先判断是否为 Node 内置模块(如 fs、http)
- 若不是则按照
module.paths依次查找,找到对应目录下的 package.json 文件里的main作为入口文件。 - 若
main错误或package.json不存在则尝试读取对应目录下的index.js/json/node文件。
require 过程
- 可以看到
require内部调用了Module._load方法用于加载模块 Module._load:- 首先到
Module._cache中查找缓存 - 若没有则创建一个新的
Module实例 - 使用
module.load(Module.prototype.load)加载指定文件,module.load内会调用module._compile(Module.prototype._compile)执行代码。 module._compile为同步代码,故module.load需等待模块代码执行完毕才会返回module.exports。
- 首先到
Module.prototype._compile:- 通过
makeRequireFunction生成一个require函数,生成的这个require函数内部其实还是调用module.require(Module.prototype.require),只是裹了一层,之后还将resolvemaincache挂载到了导出的require上。 - 模块代码被包裹到一个函数中执行,避免全局环境污染,并给定参数
requireexportsmodule__dirname__filename。
- 通过