HTML中的脚本加载
浏览器中 script、defer、async 与模块脚本的加载方式。
#type / concept
#status / growing
#resource / html
#resource / javascript
#platform / browser
[!info] related notes
HTML中的脚本加载
这篇笔记关注浏览器脚本加载层,不讨论 ECMAScript 语法本身。
三种加载模式
普通 script
<script src="./main.js"></script>
浏览器遇到时会:停止解析 → 下载 → 执行 → 继续解析。这叫解析阻塞,因为 JS 可能修改 DOM。
defer
<script src="app.js" defer></script>
- 脚本并行下载
- HTML 继续解析
- 等 HTML 全部解析完成后再按顺序执行
- 适合依赖 DOM 的页面脚本
async
<script src="app.js" async></script>
- 脚本并行下载
- 下载完立刻执行,执行时可能打断 HTML 解析
- 多个 async 脚本执行顺序不保证
模块脚本
<script type="module" src="./main.js"></script>
- 默认延迟执行
- 顶层
this不是window - 默认严格模式
- 变量不会污染全局
- 支持
import/export
速查对比
| 类型 | 下载 | 执行时机 | 阻塞解析 | 顺序保证 |
|---|---|---|---|---|
| 普通 | 阻塞 | 立即 | 是 | 是 |
| defer | 并行 | DOM 解析完 | 否 | 是 |
| async | 并行 | 下载完立即 | 可能打断 | 不保证 |
| module | 并行 | 默认延迟 | 否 | 是 |
重点问题
- 普通
<script>如何阻塞解析 defer和async的区别type="module"为什么改变脚本加载与作用域方式- 动态插入脚本有什么行为差异
面试要点
来自 defer-vs-async-interview-question 的面试视角整理。
一句话回答
defer 和 async 都能让脚本并行下载,但 defer 会等 HTML 解析完成后按顺序执行,async 则是下载完就立刻执行,顺序不保证。
最稳的回答主线
defer
- 并行下载
- 不阻塞 HTML 继续解析
- DOM 解析完成后再执行
- 多个
defer脚本顺序可控
async
- 并行下载
- 谁先下完谁先执行
- 执行时可能打断 HTML 解析
- 多个脚本执行顺序不保证
场景化表达
- 依赖 DOM 或多个脚本有先后关系时,优先考虑
defer - 独立统计脚本、广告脚本这类互不依赖脚本,更常见
async
最短记忆方式
defer 是延后且有序,async 是谁快谁先执行。
放回主题图里看
- 学模块语法本体:es6-module-import-export
- 学浏览器如何加载这些脚本:当前这篇
- 学完整浏览器宿主环境:javascript-in-browser