script 中的 async 和 defer

2024-11-07 pv

现代网站中,纯 HTML 和 CSS 的静态站点很少。几乎所有的网站都需要使用 JavaScript,尤其是重用户交互的,像地图、搜索引擎等。

加载的脚本数量不仅多,而且体积大,否则不足以支撑如此复杂而精细的操作。

对用户而言,更关心的是使用是否流畅,卡顿、白屏,几乎不可忍受。

因此,对于网站开发者,一方面要利用 JavaScript 灵活的能力,另一方面要尽可能提升用户体验。

asyncdefer 是两个常用的,优化脚本加载的手段。

1. 浏览器加载 JavaScript 的方式

在我刚开始学习前端开发的时候,大概是 2016 年。

当时书中提到的”最佳实践“:将 <script> 放在 <body> 的最后,而不是 <head> 里。

这是由浏览器加载的流程决定的。

浏览器在解析 HTML 时,顺序执行。如果遇到 <script>,暂停解析,加载并执行该脚本,然后回过头来继续处理 HTML。

这是一种同步的处理方式,如果脚本过多,或较大,就会阻塞很长时间。

尽管浏览器已经忙得焦头烂额,用户看到的,却是白屏。

这篇 文章🔗 有更详尽地介绍。

为了解决这个问题,现代浏览器提供了 asyncdefer 两个关键词。

2. async 和 defer 的工作原理

async 即异步。在 <script> 标签里加入这个字段,是提示浏览器,这段脚本允许异步下载,不要阻塞 HTML 的解析。

异步加载完成,立即执行。由于是异步的,多个 async 的顺序无法得到保证。

因此 async 适合那种相互之间没有依赖关系,独立,与 HTML 内容无关的脚本,例如站点分析、广告投放等。

async 不仅提高了加载的并行度,而且不影响页面解析,从而有效提高页面渲染速度。

可见,解耦(Decouple)除了在面向对象中使用,对前端开发也有借鉴意义。

defer 是推迟。标记 defer 的脚本会在页面加载完成后,按照在页面中出现的顺序依次执行,但早于 DOMContentLoaded🔗 事件。同样,defer 不会阻塞页面加载。

这样的好处是,脚本执行的顺序有保障,尤其对于那种有依赖关系的脚本。

另外,如果脚本需要操作页面,defer 是必要的,因为它总是在 HTML 解析完成后被执行。

3. 最佳实践

asyncdefer 是两种不同的提高页面加载速度的方法。

都是通过不阻塞 HTML 解析实现。

但实现的方式略有差异,使用场景也不相同。

简单总结:

  • async 适合独立工作的脚本,不依赖页面内容
  • defer 则需要获取页面信息后才能工作,或对那些需要保证执行顺序的脚本

对于简短的脚本,放置在 <body> 尾部依然是个好的选择。

现在,<script> 支持一种新的 导入方式🔗:模块,通过 type="module" 实现。模块脚本默认按照 defer 的方式执行。

当然,更重要的是不要滥用 JavaScript,遵循前端开发中的“渐进增强”原则。

4. 总结

作为一名前端开发者,交付的不仅是一个页面,更是一种用户体验

作为离终端用户最近的人,了解,洞察并满足他们的需要,很重要。

毕竟,谁都不愿意在浏览网站时,遇到半天都打不开的情况。

现代浏览器提供了一系列优化手段,知其然,懂使用,遵循最佳实践,四两拨千斤,简单而有效。

从更加广义的系统设计角度,模块间的解耦,依赖关系的安排,渐进增强的实践,就更加仰仗专业前端工程师的经验了。

(完)

参考

  1. Scripts: async, defer🔗
  2. <script>: The Script element - HTML: HyperText Markup Language | MDN🔗
  3. Scripts and Event Handling | Docs🔗
在 GitHub 上编辑本页面

最后更新于: 2024-11-20T09:44:17+08:00