ESM 的 import 是如何解析和加载的?
ESM 加载流程
1. Construction(构造):下载并解析模块文件,建立模块记录
2. Instantiation(实例化):创建模块环境,建立导入导出绑定
3. Evaluation(求值):执行模块顶层代码与 CommonJS 的关键区别
CommonJS 的步骤是交织的:加载→执行→导出,同步完成。
ESM 的步骤是分离的:先加载所有依赖,再建立绑定,最后执行。
1. Construction 阶段
// ESM 在执行前先解析整个依赖树
import a from './a.mjs' // a.mjs 中可能还有 import
import b from './b.mjs'
// 1. 解析入口模块
// 2. 发现 import,递归解析依赖
// 3. 建立模块依赖图
// 4. 所有模块都下载并解析完成2. Instantiation 阶段
建立导入导出的内存绑定(不是拷贝):
// lib.mjs
export let count = 0 // 在内存中分配
export function inc() { count++ }
// app.mjs
import { count, inc } from './lib.mjs'
// count 是对 lib.mjs 中 count 的引用绑定
inc()
console.log(count) // 1(实时反映变化)3. Evaluation 阶段
按依赖顺序从底向上执行模块代码:
app.mjs → 依赖 lib.mjs → 先执行 lib.mjs → 再执行 app.mjs静态 import 的限制
// ✅ 静态分析:import 路径必须是字符串字面量
import { add } from './math.mjs'
// ❌ 不能用变量
import { add } from variable // 语法错误
// ❌ 不能在条件语句中
if (condition) {
import { add } from './math.mjs' // 语法错误
}
// ❌ 不能在函数中
function load() {
import { add } from './math.mjs' // 语法错误
}动态 import()
// import() 是异步的,返回 Promise
const module = await import('./math.mjs')
console.log(module.add(1, 2))
// 条件加载
if (feature === 'advanced') {
const advanced = await import('./advanced.mjs')
advanced.run()
}
// 按需加载(路由懒加载)
const Dashboard = lazy(() => import('./Dashboard'))import.meta
// 获取当前模块的元信息
console.log(import.meta.url) // 模块文件的 URL
// 在 Node.js 中
import { dirname } from 'path'
const __dirname = dirname(new URL(import.meta.url).pathname)导入导出的绑定
// Named export
export const PI = 3.14
export function add(a, b) { return a + b }
// Default export(每个模块只能一个)
export default class User {}
// Re-export
export { add } from './math.mjs' // 重新导出
export { default as User } from './user.mjs' // 重命名默认导出
export * from './utils.mjs' // 导出全部(不含 default)
// Import 时重命名
import { add as plus } from './math.mjs'
import { default as User } from './user.mjs'
import User from './user.mjs' // 简写浏览器中加载 ESM
<!-- 内联 ESM -->
<script type="module">
import { add } from './math.mjs'
console.log(add(1, 2))
</script>
<!-- 外部 ESM -->
<script type="module" src="./app.mjs"></script>
<!-- Nomodule 降级 -->
<script nomodule>
alert('请使用支持 ESM 的浏览器')
</script>关键点
- ESM 默认严格模式(
'use strict'不需要手写) - 顶层
this是undefined(CJS 是module.exports) - ESM 是异步加载的(Construction 阶段可以并行下载)