type="module"通过静态分析import语句自动构建依赖图并按拓扑序执行,确保被导入模块先于引用者加载;重复导入同一模块仅执行一次,但必须配合export/import语法才生效。

怎么用 type="module" 自动构建依赖图
浏览器原生支持的 type="module" 会静态分析 import 语句,自动生成执行依赖顺序,不需要你手动排 <script> 标签顺序。但前提是:必须有真实的 import 和 export。
- 错误写法:
<script type="module" src="main.js"></script>,而main.js里只有console.log('hi')—— 这个脚本会被当作带作用域隔离的普通脚本,不参与依赖图构建 - 正确写法:入口文件(如
main.js)至少含一条import,被导入文件(如utils.js)至少含一条export -
export default和具名export不能混用于同一文件的解构导入,否则报SyntaxError: Unexpected token 'export' - 重复
import同一模块(比如a.js和b.js都import './config.js'),浏览器只执行config.js一次,避免重复初始化
async 和 defer 混用为什么破坏依赖图
它们是两套互不感知的调度机制:async 脚本一下载完就插队执行,defer 脚本则排队等 DOM 解析完再按序执行。混用等于把依赖关系交给竞态条件决定。
- 典型翻车:
<script src="lib.js" defer></script>+<script src="app.js" async></script>→app.js可能在lib.js下载前就执行,报ReferenceError: $ is not defined -
async对内联脚本无效,只对带src的外部脚本起作用;defer同样不作用于内联脚本 - IE9 曾实测多个
defer脚本乱序执行;async在 IE10 以下完全不支持 - 如果必须共存,建议用动态插入 + 手动控制:
document.createElement('script'),并监听onload和onerror避免静默失败
为什么 DOM 树顺序 ≠ 实际执行依赖图
HTML 结构只是线性文本,浏览器加载器不会把它当拓扑图用。真实依赖必须靠语义提取+运行时校准,否则会漏掉关键边。
-
<script async src="a.js"></script>写在前面,可能比后面defer的b.js执行得更晚——DOM 顺序无法反映这个事实 -
<link rel="stylesheet" href="style.css">后面跟着<script src="init.js"></script>,CSSOM 未完成会阻塞该脚本,但 DOM 树里二者是平级兄弟节点,无边关系 -
srcset图片需用currentSrc判断当前设备实际加载的资源,不能直接取src或第一个srcset值 -
data-module、data-chunk等自定义属性必须显式配置解析规则,否则工具和手动遍历都会忽略
fallback 时 nomodule 怎么不重复执行
旧浏览器(如 IE)不认识 type="module",会跳过它;现代浏览器则会忽略 nomodule。但顺序错位会导致 Safari 旧版同时执行两个版本。
立即学习“前端免费学习笔记(深入)”;
- 必须把
<script nomodule src="legacy.js"></script>放在所有type="module"脚本之后,不能前置 -
nomodule脚本不能含import或export,得是 IIFE 或 CommonJS 形式,否则在老浏览器里直接语法报错 - 双击打开本地 HTML 文件(
file://协议)时,模块脚本因 CORS 被拒,此时nomodule是唯一可用路径,务必验证其逻辑完整性 - 不要假设
nomodule环境里已有全局变量(比如jQuery),要自行检测或封装兜底逻辑
type="module",如果入口没 import,或者 fallback 脚本没做环境隔离,图就还是断的。



















