script 中的 async 和 defer
2024-11-07
现代网站中,纯 HTML 和 CSS 的静态站点很少。几乎所有的网站都需要使用 JavaScript,尤其是重用户交互的,像地图、搜索引擎等。
加载的脚本数量不仅多,而且体积大,否则不足以支撑如此复杂而精细的操作。
对用户而言,更关心的是使用是否流畅,卡顿、白屏,几乎不可忍受。
因此,对于网站开发者,一方面要利用 JavaScript 灵活的能力,另一方面要尽可能提升用户体验。
async
和 defer
是两个常用的,优化脚本加载的手段。
1. 浏览器加载 JavaScript 的方式
在我刚开始学习前端开发的时候,大概是 2016 年。
当时书中提到的”最佳实践“:将 <script>
放在 <body>
的最后,而不是 <head>
里。
这是由浏览器加载的流程决定的。
浏览器在解析 HTML 时,顺序执行。如果遇到 <script>
,暂停解析,加载并执行该脚本,然后回过头来继续处理 HTML。
这是一种同步的处理方式,如果脚本过多,或较大,就会阻塞很长时间。
尽管浏览器已经忙得焦头烂额,用户看到的,却是白屏。
这篇 文章🔗 有更详尽地介绍。
为了解决这个问题,现代浏览器提供了 async
和 defer
两个关键词。
2. async 和 defer 的工作原理
async
即异步。在 <script>
标签里加入这个字段,是提示浏览器,这段脚本允许异步下载,不要阻塞 HTML 的解析。
异步加载完成,立即执行。由于是异步的,多个 async
的顺序无法得到保证。
因此 async
适合那种相互之间没有依赖关系,独立,与 HTML 内容无关的脚本,例如站点分析、广告投放等。
async
不仅提高了加载的并行度,而且不影响页面解析,从而有效提高页面渲染速度。
可见,解耦(Decouple)除了在面向对象中使用,对前端开发也有借鉴意义。
defer
是推迟。标记 defer
的脚本会在页面加载完成后,按照在页面中出现的顺序依次执行,但早于 DOMContentLoaded
🔗 事件。同样,defer
不会阻塞页面加载。
这样的好处是,脚本执行的顺序有保障,尤其对于那种有依赖关系的脚本。
另外,如果脚本需要操作页面,defer
是必要的,因为它总是在 HTML 解析完成后被执行。
3. 最佳实践
async
和 defer
是两种不同的提高页面加载速度的方法。
都是通过不阻塞 HTML 解析实现。
但实现的方式略有差异,使用场景也不相同。
简单总结:
async
适合独立工作的脚本,不依赖页面内容defer
则需要获取页面信息后才能工作,或对那些需要保证执行顺序的脚本
对于简短的脚本,放置在 <body>
尾部依然是个好的选择。
现在,<script>
支持一种新的 导入方式🔗:模块,通过 type="module"
实现。模块脚本默认按照 defer
的方式执行。
当然,更重要的是不要滥用 JavaScript,遵循前端开发中的“渐进增强”原则。
4. 总结
作为一名前端开发者,交付的不仅是一个页面,更是一种用户体验。
作为离终端用户最近的人,了解,洞察并满足他们的需要,很重要。
毕竟,谁都不愿意在浏览网站时,遇到半天都打不开的情况。
现代浏览器提供了一系列优化手段,知其然,懂使用,遵循最佳实践,四两拨千斤,简单而有效。
从更加广义的系统设计角度,模块间的解耦,依赖关系的安排,渐进增强的实践,就更加仰仗专业前端工程师的经验了。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2024/async-defer/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2024-11-20T09:44:17+08:00