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>

关键点

同分类其他题目