ES6模块是官方标准化的静态模块机制,加载时确定依赖关系,支持tree-shaking与循环依赖绑定,具名导出为实时绑定,default导出为值绑定,需注意Node.js启用条件及与CommonJS互操作限制。

JavaScript 的模块系统不是“一个东西”,而是随着语言演进逐步形成的几套方案;ES6 模块(import/export)是官方标准化的静态模块机制,不是语法糖,也不等价于 CommonJS。
ES6 模块是静态的,加载时就确定依赖关系
和 require() 不同,import 语句必须出现在模块顶层,不能放在 if 或函数里;浏览器和打包工具(如 Webpack、Vite)在解析阶段就能分析出整个依赖图,从而做 tree-shaking、循环依赖检测、预加载优化等。
-
import不能动态拼接路径:import('./' + name + '.js')是动态导入(import()函数),属于另一机制,返回 Promise - 即使写成
import { foo } from './utils.js';,实际加载和执行顺序仍由模块图决定,不是“遇到就立刻执行” - Node.js 中启用 ES 模块需满足:文件后缀为
.mjs,或package.json中设置"type": "module"
默认导出与具名导出行为不同,容易混淆
export default 导出的是一个值的“引用绑定”,而 export 具名导出是按名称绑定的实时绑定。这意味着:
- 具名导出在模块内可被重新赋值,且其他模块能感知到更新(仅限于原始类型以外的可变对象)
- 默认导出一旦初始化完成,就不再响应内部变量变化(除非导出的是对象本身)
- 重命名导入时,
import xxx from 'mod'对应export default,而import { xxx } from 'mod'对应export const xxx = ...
export let count = 0;
export function increment() {
count++;
}
export default { version: '1.0' };
// 另一文件中:
import { count, increment }, mod from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1 ← 能看到变化
console.log(mod.version); // '1.0'
ES6 模块天然支持循环依赖,但行为与 CommonJS 不同
CommonJS 在循环 require 时返回已执行部分的 exports 对象(可能不完整);ES6 模块则在实例化阶段就建立绑定,所有导入都是“实时只读视图”,即使模块尚未执行完,也能安全访问已声明的导出绑定。
一、外卖通叫餐(预订)系统单店版是什么样的一个系统? 外卖通系列软件是针对非商品性买卖、有别于传统的商城系统的、外卖和预订为概念性的店铺管理系统,我们的口号就是:让所有的门店在网上安个家,以往的版本都是基于多用户性质的平台系统,而外卖通单店版是基于某个店铺的专业外卖预订管理系统,设计了外卖、预订、专题活动、小游戏、资讯、形象、点评、积分、相册等多种功能模块以适应商家办站的各种需求。这套系统可
立即学习“Java免费学习笔记(深入)”;
- 循环依赖中,若 A
import { x } from './B.js',Bimport { y } from './A.js',只要 x/y 是顶层声明(const/let/function),就不会报错 - 但如果 B 尝试读取 A 中尚未执行到的变量(比如
const x = something();还没运行),此时访问会抛ReferenceError - 这种“延迟求值+绑定可见性”的设计让调试更可预测,但也要求开发者理解模块生命周期(parse → instantiate → evaluate)
真正难的不是写 import 和 export,而是理解它如何改变代码的加载时机、作用域隔离方式和热更新表现——尤其在混合使用 require 和 import、或在非构建环境中直接跑 .mjs 时,错误信息往往模糊,比如 ERR_REQUIRE_ESM 或 Cannot use import statement outside a module,背后其实是模块系统切换的边界没对齐。









