es6模块导入缓存机制是“模块单例+首次执行+路径映射”:同一规范路径模块仅执行一次顶层代码,后续import均复用缓存导出对象,且导出为活绑定而非值拷贝。

ES6 模块的导入缓存机制,本质是“模块单例 + 首次执行 + 路径映射”。它不是开发者手动控制的缓存,而是 JavaScript 引擎在加载阶段自动完成的底层行为:同一个模块路径,只执行一次顶层代码,后续所有 import 都复用同一份导出对象。
模块只执行一次,无论导入多少次
只要 import 的路径完全一致(包括协议、域名、端口、查询参数、哈希),引擎就认为是同一个模块。首次导入时,会同步执行该模块全部顶层语句(包括变量声明、函数定义、副作用代码),然后把 整个模块的导出对象 存入内部缓存。之后任何地方再 import 它,都不再运行代码,而是直接返回缓存中的导出对象。
- 哪怕只 import 一个变量,整个模块也会完整执行
- 哪怕导入后完全不使用,执行仍会发生
- 不同文件中多次 import './utils.js',utils.js 只执行一次
缓存以模块规范路径为键,不是文件内容
缓存依据的是解析后的规范模块说明符(Resolved Specifier),而非原始字符串或文件内容。这意味着:
-
import { a } from './index.js'和import { b } from './index.js'共享同一缓存实例 -
import m from './mod.js'和import { default as m } from './mod.js'指向相同模块 - 但
import m from './mod.js'和import m from '../sub/mod.js'(即使指向同一物理文件)会被视为两个不同模块,各自执行、各自缓存
导出值是活绑定,不是拷贝
ES6 模块导出的不是值的快照,而是对顶层声明变量的实时引用(live binding)。如果模块内导出一个 let 变量,其他模块 import 后修改它,所有导入方都能看到变化;但如果导出的是 const 或字面量,则不可变。
-
export let count = 0;→ 所有导入方共享同一个count绑定 -
export const PI = 3.14;→ 导入方拿到的是不可重新赋值的常量 -
export default { name: 'a' };→ 导入方拿到对象引用,可修改其属性
默认导出也遵循同一缓存规则
export default 不改变缓存逻辑。它只是语法糖,用于指定模块的主出口项。模块依然只执行一次,default 导出的内容和其他命名导出一样,都来自那次唯一执行的结果。
- 即使只 import 默认导出,模块内所有命名导出(如
export const VERSION)也已定义并缓存 - 混合导出(default + named)时,
import A, { B } from './m.js'中 A 和 B 来自同一模块实例 - default 导出可以是立即执行表达式结果,但该表达式仅在模块首次求值时运行一次











