ES6模块导入导出方式

ESM 的 import / export 语法与模块边界。

#type / concept #status / growing #resource / javascript #resource / ecmascript

[!info] related notes

ES6 模块 (ESM)

一句话定义

ESM 是 ECMAScript 原生的模块系统,通过静态的 import / export 语法实现模块间依赖声明,支持静态分析和 Tree Shaking。

核心机制 / 工作原理

  1. 每个文件是一个模块,拥有独立作用域,模块内的变量不会污染全局。
  2. 模块只执行一次并缓存结果(单例),多次导入同一模块得到相同引用。
  3. import 语句是静态的(编译时确定依赖关系),这使打包工具可以做 Tree Shaking。
  4. ESM 导出的是 活绑定 (live bindings):导入方看到的是导出变量的引用,而非值的拷贝。

命名导出 / 导入

// ---- math.js ----
export const PI = 3.14159;
export function add(a, b) { return a + b; }

// ---- app.js ----
import { PI, add } from './math.js';
import { add as sum } from './math.js'; // 重命名导入
import * as math from './math.js';       // 命名空间导入

默认导出 / 导入

// ---- logger.js ----
export default function log(msg) {
  console.log(msg);
}

// ---- app.js ----
import log from './logger.js'; // 名字可以随便取
import myLog from './logger.js';

重新导出 (Re-export)

export { add } from './math.js';              // 转发命名导出
export { default as Logger } from './logger.js'; // 转发默认导出
export * from './math.js';                     // 转发所有命名导出(不含 default)
export * as math from './math.js';             // ES2020: 命名空间再导出

动态导入

const module = await import('./heavy-module.js');
module.doSomething();

// 条件导入
if (needsChart) {
  const { Chart } = await import('./chart.js');
  new Chart(canvas);
}

活绑定 (Live Bindings)

// counter.js
export let count = 0;
export function increment() { count++; }

// app.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1 — 活绑定,看到的是最新值

循环依赖行为

ESM 能处理循环依赖,但有一个关键限制:如果模块 A 还没执行完就被 B 导入,B 拿到的是 A 当前已导出的部分(可能是未初始化的值)

// a.js
import { b } from './b.js';
export const a = 'a';
console.log('b in a:', b); // undefined — b.js 还没执行完

// b.js
import { a } from './a.js';
export const b = 'b';
console.log('a in b:', a); // "a" — a.js 已执行完

ESM vs CJS 快速对比

维度ESMCJS
语法import / exportrequire() / module.exports
加载时机编译时(静态)运行时(动态)
绑定方式活绑定(引用)值拷贝
Tree Shaking支持不支持
浏览器原生支持(<script type="module">不支持
循环依赖部分支持(活绑定)可能拿到不完整对象

边界与常见误解

  • import 必须在顶层:常规 import 不能在 if 或函数内使用(静态分析限制),条件加载请用 import()
  • 默认导出不是命名导出的语法糖:一个模块可以同时有默认导出和命名导出,它们是不同的导出槽位。
  • Node.js 中 ESM 需要配置:要么文件扩展名为 .mjs,要么 package.json 中设置 "type": "module"
创建于 2025/1/1 更新于 2026/5/27