async vs defer 的区别

简介: async vs defer 的区别

1、why 两者

从事前端的小伙伴应该都知道,浏览器的渲染引擎负责解析服务器返回的一些 html、css 等资源,这些资源在未被解析之前都是以文本的形式存在,需要浏览器将对应的 html、css 解析成对应的 DOM、CSSOM 树,然后会将 DOM 和 CSSOM 合成为一棵 render 树,再经过 布局、绘制、合成 三个步骤将页面最总显示出来。


有一个问题,就是在浏览器解析 html 的过程中,如果遇到 <script> 标签的时候,浏览器就会停止解析 html,转而去执行 <script> 标签中的脚本,如果脚本中含有网络请求,那就会等网络请求完后再执行脚本中的代码,然后再回头继续解析 html。这样就造成了 html 解析被阻塞,从而导致页面渲染变慢。更严重是,如果在脚本中网络请求的资源恰好在一台网络情况较差的服务器上,这样整个网页的加载都会收到很大的影响。


举个例子:如果页面的 html 中,嵌入了一个 <script src="a.js"></script> 的标签,浏览器在解析到该便签时主要会做以下几个步骤:1. 停止解析 html、2.执行 a.js 中的脚本、3.如果 a.js 中有其他资源,则继续请求并执行、4.继续解析 html,由此就阻碍了 html 的解析。


为了针对上述的问题,就出现了 async 和 defer 两种解决方式。首先简要介绍一下:


两者都会并行下载 js,不会影响页面的解析,不同的是:


defer 会按照顺序在 DOMContentLoaded 前按照页面出现顺序依次执行脚本。

async 则是下载完立即执行。

如果遇到 js 脚本中有 document.write(),则在上述两者的使用时,浏览器会发出警告。

2、async

对于普通脚本,如果存在 async 属性,那么普通脚本会被并行请求,并尽快解析和执行。

对于模块脚本,如果存在 async 属性,那么脚本及其所有依赖都会在延缓队列中执行,因此它们会被并行请求,并尽快解析和执行。

该属性能够消除解析阻塞的 Javascript。举个例子:

<script src="b.js"async></script>
<script src="c.js"async></script>
  • 不阻止解析 document, 并行下载 b.js,c.js
  • 当脚本下载完后立即执行,两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后

3、defer

这个布尔属性被设定用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。

defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且解析完成。举个例子:

<script src="d.js" defer></script>
<script src="e.js" defer></script>
  • 不阻止解析 document, 并行下载 d.js,e.js
  • 即使下载完 d.js, e.js 仍继续解析 document
  • 按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前 依次执行 d.js,e.js

4、总结

<script src="script.js"></script>:没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即“ 指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。


<script async src="script.js"></script>:有 async,在加载和渲染后续文档元素的过程时,将和 script.js 的加载与执行并行进行(异步),如果脚本中涉及操作 DOM 的操作就可能出现问题(DOM 还没解析完成)。


<script defer src="myscript.js"></script>:有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DoMContentLoaded 事件触发之前完成,因此 defer 适合与 DOM 有关联的脚本。


注意:


如果 script 无 src 属性,则 defer, async 会被忽略

动态添加的 script 标签隐含 async 属性

不管是 async 还是 defer,两者都只适用于外部的脚本,而且还要注意兼容性的问题,如果浏览器不能兼容,还是把 script 标签放到页面的底部比较好。

目录
相关文章
|
开发框架 JavaScript 前端开发
vue首次加载白屏问题
vue首次加载白屏问题
542 0
|
JavaScript
vue监听dom元素的宽高变化和自定义指令监听dom元素的宽高变化
vue监听dom元素的宽高变化和自定义指令监听dom元素的宽高变化
1634 0
|
前端开发 算法 API
直接在前端做 zip 压缩/解压
前段时间研究前端是如何解析 excel 表格的时候了解到 jszip 这个库,可以直接在前端对 zip 包进行压缩和解压缩,今天稍微水一篇。
|
JavaScript 前端开发 Go
async 和 defer的作用与区别
async 和 defer的作用与区别
871 164
|
存储 JSON 负载均衡
Token、Session、Cookies是什么?如何理解其不同?一文带你了解
这篇文章详细解释了Token、Session和Cookies的概念、作用及其区别,探讨了它们在维持HTTP无状态连接中用户状态的用途和优缺点,特别是Session的服务器端存储和Token的客户端存储特性,以及它们在不同场景下的应用。
Token、Session、Cookies是什么?如何理解其不同?一文带你了解
|
前端开发 JavaScript API
深度剖析:前端如何驾驭海量数据,实现流畅渲染的多种途径
深度剖析:前端如何驾驭海量数据,实现流畅渲染的多种途径
1388 4
|
JavaScript 前端开发 Java
JS中的隐式类型转换规则
JavaScript 是一门弱类型语言,变量类型在运行时会进行隐式转换。本文总结了常见的隐式转换规则,包括运算符转换、等号比较和布尔值转换等。例如,`1 + {a: 1}` 会先调用对象的 `toString()` 方法,最终结果为 `&#39;1[object Object]&#39;`。此外,还详细解析了 `undefined` 和 `null` 的运算行为,以及 `![] == []` 等特殊情况。通过这些例子,帮助开发者更好地理解 JavaScript 中的类型转换机制。
319 6
JS中的隐式类型转换规则
|
算法 JavaScript 前端开发
深入了解Vue2和Vue3的Diff算法差异!
总的来说,Vue3在Diff算法上的优化体现了更智能的静态内容处理、更高效的动态内容更新以及更灵活的内部结构。这些优化使得Vue3在运行时性能上有了显著的提升,尤其是在大型应用和复杂界面的场景下。通过不断地技术迭代和优化,Vue3为开发者提供了更高效、更易用的前端开发体验。
1417 6
|
存储 缓存 JavaScript
Vue中的钩子函数
Vue中的钩子函数
|
前端开发 JavaScript UED
前端性能优化:打造流畅用户体验的秘诀
【10月更文挑战第20天】前端性能优化:打造流畅用户体验的秘诀
333 0