ES Module
异步加载,在页面渲染完成之后再执行模块文件
构建过程
构建
- 根据文件路径进行查找和下载,并将所有文件解析模块记录
- 通过后序遍历依赖树,定位并下载依赖文件
- 模块记录储存在模块映射当中,单例,通过模块映射管理缓存(和
commonJS
类似),管理加载状态
实例化
- 由模块记录转变未模块实例,为模块分配内存空间
- 模块实例当中结合了代码和状态,状态储存在内存当中
- 采用深度优先的后续遍历方式,到依赖图的底部设置导出,把模块指向对应的内存地址
- 导入和导出都是指向相同的内存地址,实施绑定
- 导入模块是只读的不能被修改
求值
运行代码,从而把内存空间填充为真实值,由于是实时绑定,引入的文件会随之更改
使用
babel
或webpack
可以将 esm 转化成同步加载的形式
commonJS
同步加载模块,是 node
端主要的模块管理方式
模块引用流程
- 通过
resolve
算法由模块的名字找到对应的 id - 判断缓存当中是否存在该模块
- 首次加载模块,创建
module
对象,包含exports
和id
属性,之后存到缓存当中 loadModule(id, module, require)
;
resolve 算法(模块查找算法)
require('path')
通过绝对路径或者相对路径直接引用模块require('module')
引用了 node 的核心模块,是编译之后的二进制文件,在 node 进程启动的时候便已经家在到内存当中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 文件后缀