CommonJS的工作原理
当使用require(模块路径)
导入一个模块时,node会做以下两件事情(不考虑模块缓存):
- 通过模块路径找到本机文件,并读取文件内容
- 将文件中的代码放入到一个函数环境中执行,并将执行后module.exports的值作为require函数的返回结果
正是这两个步骤,使得CommonJS在node端可以良好的被支持
可以认为,CommonJS是同步的,必须要等到加载完文件并执行完代码后才能继续向后执行
模块的导出与模块的导入
什么是模块
模块就是一个JS文件,它实现了一部分功能,并隐藏自己的内部实现,同时提供了一些接口供其他模块使用
模块有两个核心要素 : 隐藏与暴露
隐藏的,是自己内部的实现
暴露的,是希望外部使用的接口
任何一个正常的模块化标准,都应该默认隐藏模块中的所有实现,而通过一些语法或api调用来暴露接口
暴露接口的过程即模块的导出
const text = "隐藏部分无法读取"
function a (){
console.log("模块1启动")
}
exports.a = a;
模块的导入
当需要使用一个模块时,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的。
当通过某种语法或api去使用一个模块时,这个过程叫做模块的导入
在nodejs中导入模块,使用相对路径,并且必须以./或../开头
const util = require("./utill.js") // 导入模块
util.a()
CommonJS规范
CommonJS使用exports
导出模块,require
导入模块
具体规范如下:
- 如果一个JS文件中存在
exports
或require
,该JS文件是一个模块 - 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
- 如果一个模块需要暴露一些API提供给外部使用,需要通过
exports
导出,exports
是一个空的对象,你可以为该对象添加任何需要导出的内容 - 如果一个模块需要导入其他模块,通过
require
实现,require
是一个函数,传入模块的路径即可返回该模块导出的整个内容
nodejs对CommonJS的实现
为了实现CommonJS规范,nodejs对模块做出了以下处理
- 为了保证高效的执行,仅加载必要的模块。nodejs只有执行到
require
函数时才会加载并执行模块 为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。
(function(){
//模块中的代码
})()
为了保证顺利的导出模块内容,nodejs做了以下处理
- 在模块开始执行前,初始化一个值
module.exports = {}
module.exports
即模块的导出值- 为了方便开发者便捷的导出,nodejs在初始化完
module.exports
后,又声明了一个变量exports = module.exports
- 在模块开始执行前,初始化一个值
暴露接口的代码
(function(module){
module.exports = {};
var exports = module.exports;
//模块中的代码
return module.exports;
})()
- 为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
如上面的代码 exports与 module.exports 是一样的,但是 return 的是module.exports ,可以直接使用 module.exports 来暴露接口 ,但是根据上面的底层代码会出现问题
function moudle1(){
console.log("模块2启动")
}
// 使用 module.exports 暴露接口
module.exports = {
moudle1: moudle1
}
// 此时exports 暴露的接口会找不到, 因为上面module.exports已经重写啦,且最后返回的是module.exports
exports.abc = 123;
调用模块
const util = require("./util2.js")
util.moudle1()
console.log(util.abc)
运行结果