【译】使用 defer 和 async 高效地加载 JavaScript

原文: flaviocopes.com/javascript-...

在 HTML 页面中加载 JS 脚本文件时,需要注意不要损害页面的加载性能。将 JS 脚本添加到 HTML 页面的位置和方式会影响加载时间。

传统上,脚本通过以下方式包含在页面中:

html 复制代码
<script src="script.js"></script>

当 HTML 解析器解析到这一行时,就会发出获取脚本的请求,并执行该脚本。

一旦这个过程完成,解析就可以恢复,并且可以分析 HTML 的其余部分。

可以想象,这个操作会对页面的加载时间产生巨大的影响。

如果脚本的加载时间比预期的要长一些,例如网络速度有点慢,或者您使用的是移动设备,连接有点不稳定,那么访问者可能会看到一个空白页,直到脚本加载完成并执行。

脚本在页面中的位置问题

当你第一次学习 HTML 时,你会被告知脚本标签位于 标签中:

html 复制代码
<html>
  <head>
    <title>Title</title>
    <script src="script.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

正如我前面所说,当解析器发现这一行时,它会去获取脚本并执行它。完成这项任务后,它会继续解析 body。

这样做不好,因为会产生大量延迟。解决这个问题的一个常用方法是将 script 标签放在页面底部,也就是结尾的 </body> 标签之前。

这样,script 脚本就可以在页面全部解析和加载完毕后再加载和执行,与将 script 脚本放在 head 标签中相比,这是一个巨大的提升。

如果你需要支持旧版浏览器,而这些浏览器又不支持 HTML 的两个相对较新的特性:async 和 defer,那么这就是您能做的最好的事情。

async 和 defer

async 和 defer 都是布尔值树形,它们的用法类似:

html 复制代码
<script async src="script.js"></script>
html 复制代码
<script defer src="script.js"></script>

如果同时指定这两个参数,在现代浏览器中 async 优先级更高,而支持 defer 但不支持 async 的旧版浏览器将退回到 defer。

For the support table, check caniuse.com for async caniuse.com/#feat=scrip... and for defer caniuse.com/#feat=scrip...

这些属性仅在页面 head 标签中使用 script 时才有意义,如果像我们上面看到的那样将 script 放在正 body 底部,则这些属性毫无用处。

性能比较

head 标签中的 script 不使用 defer 或 async

下面是将 script 放到页面 head 中加载脚本的方式,既没有使用 defer,也没有使用 async。

解析过程会暂停,直到获取并执行脚本。执行完毕后,继续解析。

body 标签中的 script 不使用 defer 或 async

下面是一个页面在不使用 ·defer 或 async 的情况下加载脚本的过程,将其放在 body 标签的末尾,就在 </body> 之前:

解析过程没有任何停顿,解析完成后,脚本将被获取并执行。解析是在下载脚本之前完成的,因此用户看到的页面比上一个示例要早得多。

head 标签中的 script 使用 async

下面是一个页面使用 async 加载脚本,并将其放在 head 标签中:

脚本以异步的方式获取,获取脚本后,HTML 解析会暂停转而去执行 script 脚本,脚本执行完成后才会恢复 HTML 的解析。

head 标签中的 script 使用 defer

下面是一个页面使用 defer 加载脚本,并将其放在 head 标签中:

脚本是异步获取的,只有在 HTML 解析完成后才会执行。

解析 HTML 结束的时间与我们将脚本放在 body 标签末尾的时间相同,但总的来说,脚本执行结束的时间要早得多,因为脚本被下载和HTML 解析是并行的。

因此,就速度而言,这是最理想的解决方案 🏆

阻塞解析

async 会阻止页面的解析,而 defer 不会。

阻塞渲染

async 和 defer 都不能保证阻塞渲染。这取决于你和你的的脚本(例如,确保脚本在 onLoad 事件后运行)。

domInteractive

标记为 defer 的脚本会在 domInteractive 事件后立即执行,而 domInteractive 事件会在 HTML 被加载、解析并构建 DOM 后发生。

此时,CSS 和图像仍处于解析和加载中的过程中。

一旦这个过程完成,浏览器将将会触发 domComplete 事件,然后触发 onLoad 事件。

domInteractive 非常重要,因为它的时间被认为是衡量加载速度的标准。更多信息,请参阅 MDN。

保持顺序

另一种情况是 defer:标记为 async 的脚本在可用时按随意顺序执行。标记为 defer 的脚本会按照标记中定义的顺序执行(解析完成后)。

最佳实践

使用脚本时,加快页面加载速度的最佳方法是将脚本放在 head 标签中,并在 script 标签中添加 defer 属性:

html 复制代码
<script defer src="script.js"></script>

这就是更快触发 domInteractive 事件的场景。

考虑到 defer 的优点,在各种情况下 async 似乎比异步更好。

除非你不介意延迟页面的首次呈现,否则请确保在解析页面时,你想要的 JavaScript 已经执行。

相关推荐
小poop4 小时前
CSS 基础与实战:从入门到精通网页美化
前端
Csvn5 小时前
运行时性能
前端
Never_every996 小时前
位图转矢量工具哪个好?实测抠抠图AI位图转矢量工具
大数据·前端
paopaokaka_luck7 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
空中湖7 小时前
用 React 写数据大屏:为什么我不建议用图表库
前端·react.js·前端框架
欣欣之王来了9 小时前
原始HTML插值:v-html指令的使用与安全风险
前端·学习·架构·项目·vue3教程
IT_陈寒9 小时前
Redis的LRU淘汰策略坑了我一整天
前端·人工智能·后端
计算机魔术师9 小时前
砍半价格、参数再涨,国产旗舰追到 Opus 鼻尖了
前端
特立独行的猫A9 小时前
10 MB 的桌面 AI 客户端:用仓颉 + 系统 WebView 替代 Electron
前端·harmonyos
修炼的dance10 小时前
白底图存JPG,sharp开mozjpeg背景杂点少近三成
前端