模块管理

ES Module

异步加载,在页面渲染完成之后再执行模块文件

构建过程

构建

  • 根据文件路径进行查找和下载,并将所有文件解析模块记录
  • 通过后序遍历依赖树,定位并下载依赖文件
  • 模块记录储存在模块映射当中,单例,通过模块映射管理缓存(和 commonJS 类似),管理加载状态

实例化

  • 由模块记录转变未模块实例,为模块分配内存空间
  • 模块实例当中结合了代码和状态,状态储存在内存当中
  • 采用深度优先的后续遍历方式,到依赖图的底部设置导出,把模块指向对应的内存地址
  • 导入和导出都是指向相同的内存地址,实施绑定
  • 导入模块是只读的不能被修改

求值

运行代码,从而把内存空间填充为真实值,由于是实时绑定,引入的文件会随之更改

使用 babelwebpack可以将 esm 转化成同步加载的形式

commonJS

同步加载模块,是 node端主要的模块管理方式

模块引用流程

  1. 通过resolve算法由模块的名字找到对应的 id
  2. 判断缓存当中是否存在该模块
  3. 首次加载模块,创建 module 对象,包含 exportsid 属性,之后存到缓存当中
  4. loadModule(id, module, require);

resolve 算法(模块查找算法)

  1. require('path') 通过绝对路径或者相对路径直接引用模块
  2. require('module') 引用了 node 的核心模块,是编译之后的二进制文件,在 node 进程启动的时候便已经家在到内存当中
  3. require('other module') 判断所有可能的路径,就是从当前文件开始往外层开始,每一层查找 node_modules 文件夹
    • 在每层的 node_modules 文件夹当中进行查找,.js -> .json -> .node
      • 之后将名称当成目录,查找该目录下面的文件 /package.json -> /index.js -> ...

二者之间的区别与互操作

区别

  • CJS 输出的是一个值的拷贝,当作原始类型的值,运行时加载,模块内部不会影响到引用的模块
  • ESM 输出的是值的引用,在编译时输出,在代码的静态解析阶段生成的只读引用,模块内部的修改会影响所有引用的模块

cjs 当中使用 esm

  • esm 是异步加载的,所以在前面使用 await import('') 变成同步
  • (esm 当中可以直接 import cjs 模块

在 node 当中使用 esm

mjs 文件后缀