基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

script 标签的 async 和 defer 属性有什么区别?

知识点图片

script 标签的 asyncdefer 属性都用于异步加载外部 JavaScript 文件,目的是为了避免脚本加载和执行阻塞 HTML 文档的解析(即避免“阻塞渲染”)。

它们的主要区别在于 执行的时机执行的顺序

为了更直观地理解,我们可以把过程分为三个部分:

  1. HTML 解析 (Parsing)
  2. 脚本下载 (Fetching)
  3. 脚本执行 (Execution)

1. 图解对比

假设 HTML 解析过程中遇到了一个 <script src="..."> 标签:

  • 普通 <script> (无属性):
    HTML 解析停止 -> 下载脚本 -> 执行脚本 -> HTML 解析继续
    (完全阻塞)

  • <script async>:
    HTML 解析继续 + 下载脚本 (并行) -> (下载完立即) HTML 解析暂停 -> 执行脚本 -> HTML 解析继续
    (下载不阻塞,执行阻塞)

  • <script defer>:
    HTML 解析继续 + 下载脚本 (并行) -> HTML 解析完成 -> 执行脚本
    (下载不阻塞,执行也不阻塞)


2. 详细区别

A. 执行时机 (When)

  • async (Asynchronous - 异步):
    • 脚本一旦下载完成,立即执行
    • 这意味着它会打断 HTML 的解析。如果脚本很小下载很快,它可能很快就打断解析;如果脚本很大,它可能在解析完很久之后才执行。
  • defer (Deferred - 推迟):
    • 脚本下载完成后不立即执行
    • 它会等到整个 HTML 文档解析完成(DOM 构建完毕),但在 DOMContentLoaded 事件触发之前执行。

B. 执行顺序 (Order)

  • async:
    • 无序。谁先下载完,谁先执行。
    • 如果你有 script Ascript B,且 B 依赖于 A,使用 async 可能会导致报错,因为 B 可能比 A 先下载完并执行。
  • defer:
    • 有序。严格按照它们在 HTML 中出现的顺序执行。
    • 即使 script Bscript A 先下载完,浏览器也会等 A 执行完再执行 B

3. 总结对比表

特性 <script> (默认) <script async> <script defer>
HTML 解析 被阻塞 (下载和执行时) 仅在执行时被阻塞 不阻塞 (直到解析完成)
脚本下载 阻塞 HTML 解析 异步 (并行) 异步 (并行)
执行时机 下载完成后立即执行 下载完成后立即执行 HTML 解析完成后执行
执行顺序 按文档顺序 网络下载速度决定 (无序) 按文档顺序
适用场景 必须立即执行且依赖 DOM 的关键脚本 统计代码、广告、不依赖 DOM 或其他脚本的独立文件 依赖 DOM 元素、依赖其他脚本、应用逻辑代码

4. 最佳实践建议

  1. 使用 defer 的情况(推荐默认选项):

    • 如果你的脚本依赖于 DOM 元素(例如需要 document.querySelector 获取元素)。
    • 如果你的脚本之间有依赖关系(例如 jQuery 插件依赖 jQuery 库)。
    • 通常建议将大部分应用逻辑脚本设置为 defer,放在 <head> 中,这样既能尽早开始下载,又不会阻塞页面渲染。
  2. 使用 async 的情况:

    • 脚本完全独立,不依赖其他脚本,也不依赖 DOM 结构。
    • 例如:Google Analytics(谷歌统计)、广告脚本、性能监控脚本。这些脚本什么时候加载完就什么时候跑,不影响页面主要功能。
  3. 都不用的情况:

    • 如果你需要脚本在页面渲染的最早期就执行(例如加载一些 CSS Polyfill 或者防止页面闪烁的脚本),且必须阻塞后续解析以确保正确性,则直接使用普通 <script>
00:00
00:00