通过前者(模块化一 - 掘金 (juejin.cn)),对CommonJS模块的规范应该有了一个初步的认识,了解为何需要模块化了(通过没有模块化所带来的问题这个角度去分析)。
[可以看看](JavaScript 高级深入浅出:四种模块化规范 - 掘金 (juejin.cn))
然后知道它是如何使用的(就是js所有内容进行一个立即执行函数的包裹(闭包--函数作用域),形参进行传参)-----对于require这块知识还是有许多东西去挖掘的,主要在与执行优先级(模块查找)、循环引用。
CommonJS--require
- 执行优先级
- 循环引用
- 模块查找
执行优先级
require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容;
就是这句话决定了你需要了解他们执行的区别
---------》核心模块的优先级仅次于缓存加载,在 Node 源码编译中,已被编译成二进制代码,所以加载核心模块,加载过程中速度最快。
---------》已 ./ ,../ 和 / 开始的标识符,会被当作文件模块处理。require() 方法会将路径转换成真实路径,并以真实路径作为索引,将编译后的结果缓存起来,第二次加载的时候会更快。
---------》自定义模块处理: 自定义模块,一般指的是非核心的模块,它可能是一个文件或者一个包,它的查找会遵循以下原则:
执行优先级也相当于解决了require的查找规则--可看链接,这里面讲的比较细致 ((64条消息) 2020-12-16 require的查找规则 模块的加载过程 require和import区别(ES和commonJS区别) commonJS和ES的交互_狗焕sama的博客-CSDN博客)
循环引用
本质就是依赖关系的管理,a need b, b need a; 如何去管理这个依赖关系呢?
解决方案就是首次执行js文件会先放入Module缓存中,再向下执行模块内容;往后再遇相同模块就是在缓存中直接进行数据读取,不需要再跳转回去。
引用了这位大佬的文章(「万字进阶」深入浅出 Commonjs 和 Es Module - 掘金 (juejin.cn))
//a.js文件
const getMes = require('./b')
console.log('我是 a 文件')
exports.say = function(){
const message = getMes()
console.log(message)
}
b.js文件
const say = require('./a')
const object = {
name:'《React进阶实践指南》',
author:'我不是外星人'
}
console.log('我是 b 文件')
module.exports = function(){
return object
}
//main.js文件
const a = require('./a')
const b = require('./b')
console.log('node 入口文件')
输出结果:我是 b 文件--->我是 a 文件--->node 入口文件
- ① 首先执行
node main.js,那么开始执行第一行require(a.js); - ② 那么首先判断
a.js有没有缓存,因为没有缓存,先加入缓存,然后执行文件 a.js (需要注意 是先加入缓存, 后执行模块内容); - ③ a.js 中执行第一行,引用 b.js。
- ④ 那么判断
b.js有没有缓存,因为没有缓存,所以加入缓存,然后执行 b.js 文件。 - ⑤ b.js 执行第一行,再一次循环引用
require(a.js)此时的 a.js 已经加入缓存,直接读取值。接下来打印console.log('我是 b 文件'),导出方法。 - ⑥ b.js 执行完毕,回到 a.js 文件,打印
console.log('我是 a 文件'),导出方法。 - ⑦ 最后回到
main.js,打印console.log('node 入口文件')完成这个流程。
exports与module.exports区别
共同点:都是指向同一个对象
区别:exports是以键值对形式存在,不能赋值(不然就无法暴露接口了);
因此如果是想要键值对象的这种格式, exports.text = …; 是可以的。
但如果是一个数组或者是函数(非对象形式). 只有module.exports = function(){ }有效。
[一些细节]((64条消息) 终于讲清楚了nodejs中exports和module.exports的区别_IT 哈的博客-CSDN博客_exports和module.exports的区别)
小小总结
通过前面的介绍对于以下几个知识点应该有比较深的理解了。
- Commonjs 如何解决的循环引用问题 ?-->本质就是模块依赖管理方式<--require的缓存机制,从缓存中获取(如果有缓存) CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会全部执行。CommonJS的做法是,一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。((64条消息) 模块循环引用_yanyang1116的博客-CSDN博客)
- 既然有了 exports,为何又出了 module.exports? 既生瑜,何生亮 ?
- require 模块查找机制 ?-->就是通过模块的优先级决定的<--不同模块导致了不同的查找机制
- exports = {} 这种写法为何无效 ?----就是引用对象改变了
ES6 Module
至此CommonJS告一段落了;下一章开始了解ES6