跳到主要内容

谈谈模块化

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则是指向同一块内存,模块实际导出的是这块内存的地址,每当用到时根据地址找到对应的内存空间,这样就实现了所谓的“动态绑定”。