模块依赖图在html解析阶段静态构建,提前扫描type="module"脚本中的import语句;依赖路径必须为字符串字面量,模块执行一次且缓存复用,加载顺序由拓扑序决定,重复导入不触发新请求,失败则中断整个依赖链。

模块依赖图在 HTML 解析阶段就静态构建
浏览器不是等脚本下载完再分析 import,而是在解析 HTML 时就扫描所有 type="module" 脚本里的 import 语句,提前生成完整的依赖图。这意味着:
- 依赖关系必须是字符串字面量——
import后面不能是变量、拼接路径或条件分支,否则直接 SyntaxError - 即使某个模块被多个地方 import,它也只执行一次,后续导入返回缓存实例(不是重新执行)
- 模块加载顺序由依赖图拓扑序决定,和
<script></script>在 HTML 中的书写顺序无关(但多个入口模块仍按 DOM 顺序执行)
重复 import 同一模块不触发重复网络请求
浏览器为每个模块 URL 维护独立缓存记录,含 ETag 和 Cache-Control。比如:
import { a } from './utils.js';
import { b } from './utils.js';
只会发起一次 GET /utils.js 请求,第二次直接复用已解析并实例化的模块对象。这和传统 <script src="utils.js"></script> 完全不同——后者每次引入都可能触发新请求、新执行、新全局污染。
注意:./utils.js 和 ../utils.js 被视为两个不同模块;大小写、查询参数(如 utils.js?v=1)也会影响缓存键。
模块加载失败会中断整个依赖链执行
模块图是强依赖的:只要某个 import 失败(404、CORS、语法错误),整个依赖链上尚未执行的模块都会被跳过,控制台报 Failed to load module script 或 TypeError: Failed to fetch dynamically imported module。
常见触发点:
-
file://协议下所有模块加载直接静默失败(连请求都不发) -
import { foo } from './utils'缺扩展名 →Failed to resolve module specifier - CORS 响应头缺失(哪怕同域服务器没配
Access-Control-Allow-Origin,某些配置下也会拦截)
动态 import() 不改变静态依赖图,但引入运行时不确定性
import() 是 Promise,只影响执行时机,不参与静态依赖图构建。它的路径仍需满足模块规则:
- 必须带扩展名:
import('./modal.js')✅,import('./modal')❌ - 不能用变量:
const name = 'utils'; import(`./${name}.js`)❌(模板字符串也不行) - 失败时抛 Promise rejection,不会中断主模块执行,但需手动
.catch()
真正“量化”的差异在于:静态 import 决定初始加载资源集和执行顺序;import() 只决定某段代码何时开始拉取、何时 resolve,不影响其余模块的加载节奏。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











