标准答案
- 普通 script 会阻塞 HTML 解析。浏览器遇到它会暂停构建 DOM,先下载并执行脚本,执行完再继续解析。
- defer 会并行下载脚本,不阻塞 HTML 解析,等文档解析完成后按页面中的顺序执行。
- async 也会并行下载,但下载完会立即执行,可能打断解析,多个 async 脚本之间没有顺序保证。
- type="module" 是模块脚本,默认行为接近 defer,会按模块依赖图加载,并且有模块作用域和自动严格模式。
- 实际开发里,业务主入口通常用 defer 或 module;独立统计、广告这类第三方脚本更适合 async;有依赖顺序的脚本不要用 async 串联。
题目解析
- 普通 script:阻塞解析,适合极少数必须立即执行的脚本。
- defer:并行下载,DOM 解析完成后按顺序执行,适合大多数业务脚本。
- async:并行下载,下载完立即执行,适合统计、广告、独立第三方脚本。
- type="module":按模块图加载,默认延迟执行,模块作用域独立,自动严格模式。
defer 脚本会按它们在文档中的出现顺序执行;async 谁先下载完谁先执行;module 脚本会先解析依赖图,执行时遵守模块依赖关系。
如果脚本之间有依赖关系,不要用 async 串联它们。
代码示例
业务入口脚本通常使用 defer 或 module,第三方统计更适合 async。
HTML
<script src="/legacy-critical.js"></script>
<script defer src="/app.js"></script>
<script async src="https://example.com/analytics.js"></script>
<script type="module" src="/main.js"></script>常见误区
- 认为 async 一定比 defer 更快。async 可能更早执行,但会打断解析,且顺序不可控。
- 认为 type="module" 和普通 script 只有语法差异。模块脚本还有作用域、严格模式、CORS 和依赖解析差异。
- 把依赖 DOM 的脚本放 head 里又不加 defer,导致读取不到节点。