谈谈模块化
AMD、CMD、UMD、commonJS(CJS)、SystemJS、IIFE、ESM
-
commonjs模块
- 应用在服务端,commonjs模块是同步加载的,无法在浏览器应用,会造成阻塞
-
AMD模块
- 异步加载模块,应用在浏览器,解决了commonjs模块阻塞浏览器问题,特点是可以提前执行模块
-
CMD模块
- 通用模块,应用在浏览器,延迟执行的特点,只用再用到某个模块的时候再去requiere加载
-
ESM模块
- 是ES6的特性,通过import/export的语法来实现ESM模块,可用于按需加载,实现tree-shaking
ESM
- 使用
import/export这种方式,支持按需加载、tree-shaking - 静态分析,在编译的时候就能确定依赖关系, 更高效的模块加载和解析
- 每一个模块只加载一次, 并执行一次,再次加载同一文件,直接从内存中读取;
ESM和CJS的区别
- 加载方式:CJS 是一个运行时加载系统,使用
require函数动态加载模块,而 ESM 是一个编译时加载系统,使用import语句静态加载模块 - 性能问题:ESM 的静态分析可以提高代码的性能,因为它可以在编译阶段就可以确定模块之间的依赖关系。而 CJS 的动态加载可能会导致一些性能问题,因为它需要在运行时动态加载模块,这会导致额外的延迟和资源开销。
- ESM支持node和浏览器,CJS因为是同步加载,所以不支持浏览器,否则会造成页面阻塞
- 输出的值方式:CJS输出的值是拷贝,ESM模块输出的值是引用
CommonJS多次引入模块,会执行多次吗
- 由于缓存,一个模块不会被多次执行
- require会优先检查缓存,从缓存中加载,直接读取缓存,而不会再去执行一次
CommonJS和ESM对于循环引用的解决原理是什么?
CommonJS如何处理循环引用
通过模块缓存
CommonJS的引入特点是值的拷贝,简单来说就是把导出值复制一份,放到一块新的内存中, 进行模块缓存
- 对于模块之间的互相引用,本应是个死循环,但是实际并没有,因为CommonJS做了特殊处理——模块缓存。
- require会优先检查缓存,从缓存中加载,这样就不会出现死循环;借助缓存,输出的值也很简单就能找到了
ES Module如何处理循环引用
通过引用类型,直接在内存中读取
- ES Module导出的是值的引用
- ES Module指向同一块内存,模块实际导出的是这块内存的地址,每当用到时根据地址找到对应的内存空间,直接在内存中读取
- ES Module 会根据import和export来构建 (Module Map)模块记录,每个模块都会在模块记录中进行标记
ES Module导出的是一份值的引用,CommonJS则是一份值的拷贝。也就是说,CommonJS是把暴露的对象拷贝一份,放在新的一块内存中,每次直接在新的内存中取值,所以对变量修改没有办法同步;而ES Module则是指向同一块内存,模块实际导出的是这块内存的地址,每当用到时根据地址找到对应的内存空间,这样就实现了所谓的“动态绑定”。