模块化二

114 阅读4分钟

通过前者(模块化一 - 掘金 (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 入口文件

image.png

  • ① 首先执行 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