CommonJS的工作原理

当使用require(模块路径)导入一个模块时,node会做以下两件事情(不考虑模块缓存):

  1. 通过模块路径找到本机文件,并读取文件内容
  2. 将文件中的代码放入到一个函数环境中执行,并将执行后module.exports的值作为require函数的返回结果


正是这两个步骤,使得CommonJS在node端可以良好的被支持

可以认为,CommonJS是同步的,必须要等到加载完文件并执行完代码后才能继续向后执行

模块的导出与模块的导入

什么是模块

模块就是一个JS文件,它实现了一部分功能,并隐藏自己的内部实现,同时提供了一些接口供其他模块使用
模块有两个核心要素 : 隐藏与暴露
隐藏的,是自己内部的实现
暴露的,是希望外部使用的接口
任何一个正常的模块化标准,都应该默认隐藏模块中的所有实现,而通过一些语法或api调用来暴露接口

暴露接口的过程即模块的导出

  1. const text = "隐藏部分无法读取"
  2. function a (){
  3. console.log("模块1启动")
  4. }
  5. exports.a = a;

模块的导入

当需要使用一个模块时,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的。
当通过某种语法或api去使用一个模块时,这个过程叫做模块的导入
在nodejs中导入模块,使用相对路径,并且必须以./或../开头

  1. const util = require("./utill.js") // 导入模块
  2. util.a()

CommonJS规范

CommonJS使用exports导出模块,require导入模块
具体规范如下:

  1. 如果一个JS文件中存在exportsrequire,该JS文件是一个模块
  2. 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
  3. 如果一个模块需要暴露一些API提供给外部使用,需要通过exports导出,exports是一个空的对象,你可以为该对象添加任何需要导出的内容
  4. 如果一个模块需要导入其他模块,通过require实现,require是一个函数,传入模块的路径即可返回该模块导出的整个内容

nodejs对CommonJS的实现

为了实现CommonJS规范,nodejs对模块做出了以下处理

  1. 为了保证高效的执行,仅加载必要的模块。nodejs只有执行到require函数时才会加载并执行模块
  2. 为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。

    1. (function(){
    2. //模块中的代码
    3. })()
  3. 为了保证顺利的导出模块内容,nodejs做了以下处理

    1. 在模块开始执行前,初始化一个值module.exports = {}
    2. module.exports即模块的导出值
    3. 为了方便开发者便捷的导出,nodejs在初始化完module.exports后,又声明了一个变量exports = module.exports

暴露接口的代码

  1. (function(module){
  2. module.exports = {};
  3. var exports = module.exports;
  4. //模块中的代码
  5. return module.exports;
  6. })()
  1. 为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果

如上面的代码 exports与 module.exports 是一样的,但是 return 的是module.exports ,可以直接使用 module.exports 来暴露接口 ,但是根据上面的底层代码会出现问题

  1. function moudle1(){
  2. console.log("模块2启动")
  3. }
  4. // 使用 module.exports 暴露接口
  5. module.exports = {
  6. moudle1: moudle1
  7. }
  8. // 此时exports 暴露的接口会找不到, 因为上面module.exports已经重写啦,且最后返回的是module.exports
  9. exports.abc = 123;

调用模块

  1. const util = require("./util2.js")
  2. util.moudle1()
  3. console.log(util.abc)

运行结果
image.png