前端秘密武器:async 与 defer,你用对了吗?

前端秘密武器:async 与 defer,你用对了吗?

在前端开发的江湖里,HTML、CSS 和 JavaScript 就是我们手中的三把利刃。而在使用 HTML 引入 JavaScript 文件时,async 和 defer 这两个属性就像是隐藏在利刃中的神秘符文,掌握它们的奥秘,能让我们的代码在性能和加载顺序上如鱼得水。今天,咱们就来揭开这两个属性的神秘面纱,看看它们到底有啥区别,又该怎么根据需求来选择使用。

开篇:前端性能的痛点

在正式介绍 async 和 defer 之前,咱们先聊聊前端开发中一个让人头疼的问题------页面加载性能。想象一下,你打开一个网页,页面半天都加载不出来,你是不是会急得直跺脚?这其中很大一部分原因可能就是 JavaScript 文件的加载和执行方式出了问题。

在 HTML 中,当浏览器遇到 <script> 标签时,默认情况下会暂停 HTML 的解析,去下载并执行 JavaScript 文件,直到这个文件执行完毕,才会继续解析 HTML。这就好比你在盖房子,盖到一半突然停下来去买材料,等材料买回来用完了才接着盖,这速度能快吗?所以,为了解决这个问题,async 和 defer 这两个属性就应运而生了。

认识 <script> 标签

在 HTML 中,我们通常使用 <script> 标签来引入外部的 JavaScript 文件。基本的语法如下:

html 复制代码
<!-- 这是一个简单的引入外部 JavaScript 文件的 <script> 标签 -->
<script src="example.js"></script>

这里的 src 属性指定了要引入的 JavaScript 文件的路径。当浏览器遇到这个标签时,会按照默认的方式来处理,也就是暂停 HTML 解析,下载并执行 JavaScript 文件。

深入了解 async 属性

什么是 async 属性?

async 是 <script> 标签的一个布尔属性,当你给 <script> 标签加上 async 属性后,浏览器会异步地下载 JavaScript 文件。也就是说,浏览器不会因为遇到这个 <script> 标签而暂停 HTML 的解析,而是会继续解析 HTML,同时在后台下载 JavaScript 文件。当文件下载完成后,会立即暂停 HTML 解析,执行这个 JavaScript 文件。

代码示例

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Async Example</title>
    <!-- 这里使用了 async 属性,浏览器会异步下载这个 JavaScript 文件 -->
    <script async src="async.js"></script>
</head>

<body>
    <h1>Welcome to the Async World!</h1>
    <p>This is a paragraph to demonstrate the async behavior.</p>
</body>

</html>

特点总结

  • 异步下载:不会阻塞 HTML 解析,浏览器会在后台下载 JavaScript 文件。
  • 立即执行:文件下载完成后,会立即暂停 HTML 解析,执行该文件。
  • 加载顺序不确定 :如果有多个带有 async 属性的 <script> 标签,它们的加载顺序是不确定的,取决于哪个文件先下载完成。

使用场景

async 属性适合那些不依赖于其他脚本文件,并且不需要按照特定顺序执行的脚本。比如一些第三方的广告脚本、分析脚本等,这些脚本通常只需要在页面加载过程中某个时刻执行即可,不需要和其他脚本有严格的执行顺序。

深入了解 defer 属性

什么是 defer 属性?

defer 同样是 <script> 标签的一个布尔属性。当你给 <script> 标签加上 defer 属性后,浏览器会异步地下载 JavaScript 文件,但是不会立即执行,而是会等到 HTML 解析完成后,再按照 <script> 标签在 HTML 中出现的顺序依次执行这些脚本。

代码示例

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Defer Example</title>
    <!-- 这里使用了 defer 属性,浏览器会异步下载这个 JavaScript 文件 -->
    <script defer src="defer.js"></script>
</head>

<body>
    <h1>Welcome to the Defer World!</h1>
    <p>This is a paragraph to demonstrate the defer behavior.</p>
</body>

</html>

特点总结

  • 异步下载 :和 async 一样,不会阻塞 HTML 解析,浏览器会在后台下载 JavaScript 文件。
  • 延迟执行:文件下载完成后,不会立即执行,而是等到 HTML 解析完成后再执行。
  • 顺序执行 :如果有多个带有 defer 属性的 <script> 标签,它们会按照在 HTML 中出现的顺序依次执行。

使用场景

defer 属性适合那些依赖于 HTML 结构,并且需要按照特定顺序执行的脚本。比如一些需要操作 DOM 元素的脚本,因为这些脚本需要在 HTML 解析完成后才能正确地找到并操作相应的 DOM 元素。

async 与 defer 的对比

加载和执行顺序对比

为了更直观地理解 async 和 defer 的区别,我们来看一个对比示例。

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Async vs Defer</title>
    <!-- 第一个带有 async 属性的脚本 -->
    <script async src="async1.js"></script>
    <!-- 第二个带有 async 属性的脚本 -->
    <script async src="async2.js"></script>
    <!-- 第一个带有 defer 属性的脚本 -->
    <script defer src="defer1.js"></script>
    <!-- 第二个带有 defer 属性的脚本 -->
    <script defer src="defer2.js"></script>
</head>

<body>
    <h1>Async vs Defer Comparison</h1>
    <p>This is a paragraph to demonstrate the difference between async and defer.</p>
</body>

</html>

在这个示例中,两个带有 async 属性的脚本会异步下载,下载完成后会立即执行,它们的执行顺序不确定,取决于哪个文件先下载完成。而两个带有 defer 属性的脚本会异步下载,下载完成后不会立即执行,而是等到 HTML 解析完成后,按照在 HTML 中出现的顺序依次执行,也就是先执行 defer1.js,再执行 defer2.js。

性能影响对比

从性能角度来看,async 和 defer 都能提高页面的加载性能,因为它们都允许浏览器在后台异步下载 JavaScript 文件,不会阻塞 HTML 解析。但是,由于 async 脚本下载完成后会立即执行,可能会在 HTML 解析过程中打断解析,导致页面渲染出现卡顿。而 defer 脚本会等到 HTML 解析完成后再执行,不会影响页面的渲染,所以在某些情况下,defer 可能会有更好的性能表现。

根据需求选择 async 或 defer

不需要特定顺序且不依赖 DOM 的脚本

如果你有一些脚本不依赖于其他脚本,也不需要操作 DOM 元素,只需要在页面加载过程中某个时刻执行即可,那么可以使用 async 属性。比如:

html 复制代码
<!-- 引入第三方广告脚本,使用 async 属性 -->
<script async src="https://example.com/ads.js"></script>

需要特定顺序且依赖 DOM 的脚本

如果你有一些脚本依赖于其他脚本,或者需要操作 DOM 元素,那么应该使用 defer 属性。比如:

html 复制代码
<!-- 引入一个需要操作 DOM 的脚本,使用 defer 属性 -->
<script defer src="dom-manipulation.js"></script>

多个脚本的情况

如果有多个脚本,并且它们之间有依赖关系,需要按照特定顺序执行,那么应该使用 defer 属性。如果这些脚本之间没有依赖关系,并且不需要按照特定顺序执行,那么可以使用 async 属性。

实战案例分析

案例一:优化页面加载性能

假设你有一个电商网站,页面上需要引入一些第三方的分析脚本和一些自定义的 JavaScript 脚本。分析脚本不需要和其他脚本有严格的执行顺序,而自定义脚本需要操作 DOM 元素。那么可以这样优化:

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>E-commerce Website</title>
    <!-- 引入第三方分析脚本,使用 async 属性 -->
    <script async src="https://example.com/analytics.js"></script>
    <!-- 引入自定义脚本,使用 defer 属性 -->
    <script defer src="custom.js"></script>
</head>

<body>
    <h1>Welcome to our E-commerce Store!</h1>
    <p>Explore our amazing products.</p>
</body>

</html>

案例二:解决脚本依赖问题

假设你有一个网站,需要引入 jQuery 库和一些依赖于 jQuery 的自定义脚本。由于自定义脚本依赖于 jQuery,所以需要确保 jQuery 先加载并执行。可以这样做:

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Website with jQuery</title>
    <!-- 引入 jQuery 库,使用 defer 属性 -->
    <script defer src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- 引入依赖于 jQuery 的自定义脚本,使用 defer 属性 -->
    <script defer src="custom-jquery.js"></script>
</head>

<body>
    <h1>Welcome to our Website!</h1>
    <p>Check out our cool features.</p>
</body>

</html>

那么 ,在实际项目开发中使用

依据脚本依赖关系进行选择

  • 无依赖的脚本 :当脚本不依赖于其他脚本,并且执行顺序不会影响页面功能时,可使用 async 属性。像第三方广告脚本、社交媒体分享按钮脚本等,它们独立运行,无需与其他脚本协同工作。
html 复制代码
<!-- 引入第三方广告脚本,使用 async 属性 -->
<script async src="https://example.com/ads.js"></script>
  • 有依赖的脚本 :若脚本依赖于其他脚本,或者需要在特定脚本之后执行,那么使用 defer 属性。比如 jQuery 库和依赖于它的自定义脚本,要保证 jQuery 先加载并执行。
html 复制代码
<!-- 引入 jQuery 库,使用 defer 属性 -->
<script defer src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- 引入依赖于 jQuery 的自定义脚本,使用 defer 属性 -->
<script defer src="custom-jquery.js"></script>

根据脚本对 DOM 的操作需求来选择

  • 不操作 DOM 的脚本 :对于那些不涉及 DOM 操作的脚本,使用 async 属性可以让脚本在后台异步下载,下载完成后立即执行,不会影响页面的渲染。
html 复制代码
<!-- 引入不操作 DOM 的脚本,使用 async 属性 -->
<script async src="analytics.js"></script>
  • 操作 DOM 的脚本 :需要操作 DOM 元素的脚本,要确保在 HTML 解析完成后执行,此时应使用 defer 属性。
html 复制代码
<!-- 引入需要操作 DOM 的脚本,使用 defer 属性 -->
<script defer src="dom-manipulation.js"></script>

多个脚本的情况

  • 无顺序要求 :若多个脚本之间没有依赖关系,且不需要按照特定顺序执行,可都使用 async 属性,这样能并行下载,加快页面加载速度。
html 复制代码
<!-- 多个无依赖关系的脚本,使用 async 属性 -->
<script async src="script1.js"></script>
<script async src="script2.js"></script>
  • 有顺序要求 :当多个脚本有依赖关系,需要按照特定顺序执行时,都使用 defer 属性,它们会在 HTML 解析完成后按顺序依次执行。
html 复制代码
<!-- 多个有依赖关系的脚本,使用 defer 属性 -->
<script defer src="base.js"></script>
<script defer src="module1.js"></script>
<script defer src="module2.js"></script>

结合性能测试和优化

  • 在实际项目中,要通过性能测试工具(如 Google PageSpeed Insights、Lighthouse 等)来评估使用 async 和 defer 属性后的页面性能。根据测试结果,对脚本的加载方式进行调整和优化。
  • 对于首屏加载至关重要的脚本,可考虑将其内联到 HTML 文件中,避免额外的网络请求。而对于非首屏所需的脚本,使用 async 或 defer 属性进行异步加载。

考虑兼容性

虽然现代浏览器对 async 和 defer 属性的支持良好,但在一些旧版本的浏览器中可能存在兼容性问题。在开发时,要进行充分的兼容性测试,必要时提供降级方案。

综上所述,在实际项目开发中,要根据脚本的依赖关系、对 DOM 的操作需求、多个脚本的执行顺序、性能测试结果以及兼容性等多方面因素,综合考虑使用 async 和 defer 属性,以达到最佳的页面加载性能和用户体验。

总结

在前端开发中,合理使用 async 和 defer 属性可以显著提高页面的加载性能和用户体验。async 属性适合那些不依赖于其他脚本,并且不需要按照特定顺序执行的脚本;而 defer 属性适合那些依赖于 HTML 结构,并且需要按照特定顺序执行的脚本。通过深入理解这两个属性的区别,并根据具体需求选择合适的属性,你就能让你的前端代码更加高效、稳定。

希望这篇文章能帮助你掌握 async 和 defer 属性的使用,让你在前端开发的道路上越走越顺!如果你还有其他问题,欢迎在评论区留言讨论。

相关推荐
IT_陈寒3 小时前
Java中equals方法比了个寂寞?原来这才是正确的重写姿势
前端·人工智能·后端
默_笙3 小时前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript
CopyCode3 小时前
用 AI 迁项目有多爽?我把 Webpack 迁 Vite 的全过程记下来了
前端·架构
去伪存真3 小时前
Electron 自动化发布指南:GitHub Actions 跨平台打包全纪录
前端·electron
计算机魔术师3 小时前
OpenAI智能体失控闯进美国政府网站,53张用户图片外泄背后
前端
颜进强3 小时前
14 · NestJS ExecutionContext 执行上下文:守卫、拦截器、过滤器拿到的"同一个 context",为什么能力不一样?
前端·后端·ai编程
程序员Flycan3 小时前
🚀 跨域终结者:前端代理服务器(Proxy)原理解析与配置总结
前端
怕浪猫3 小时前
顶级模型一句话,AI 写出了能玩的 QQ飞车
前端·面试·github
huakoh3 小时前
MCP 报错分不清?先看响应里是 result 还是 error
前端
呃呃呃呃ex3 小时前
10. 现代前端工程化:ES6 React 项目实战与原理解析
前端·javascript