script标签中的async(异步)和defer(推迟)的区别
一、概念
背景:不使用async和defer属性加载外部脚本时,浏览器会立即加载并执行相应的外部脚本。这样就会阻塞了后续文档的加载。
我们经常会把script写在body底部,原因包括以下两个
- 普通的script标签的加载和解析都是同步的,会阻塞DOM的渲染。为了防止加载资源而导致的长时间的白屏,
- js可能会进行DOM操作,所以要在DOM全部渲染完后再执行。
- async
属性可选,表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本有效。
在渲染页面元素的同时下载外部脚本资源,不会阻塞页面的加载,但下载完后会立即执行脚本内容。
- defer
属性可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。在IE7及更早的版本中,对行内脚本也可以指定这个属性。
使用defer属性后,脚本会被延迟到整个页面都解析完毕后再运行。相当于告诉浏览器立即下载外部文件,但是延迟执行
外部脚本就是通过src属性引入的其它脚本文件,浏览器会对外部脚本文件进行缓存,提高页面加载速度。
行内脚本就是在script标签内书写的js代码
二、区别
1. 页面渲染的同步和异步
蓝色代表js脚本网络加载时间,红色代表 js 脚本执行时间,绿色代表 html 解析。
- 普通的script标签的加载和解析都是同步的,会阻塞DOM的渲染。
- defer属性的会在html解析的同时异步下载网络资源,直到html解析结束,才会执行,若资源在html解析完成后仍未下载完成,那么defer脚本继续加载,加载完成后直接执行
- async属性的会在html解析的同时异步下载网络资源,但下载完后会立即执行,可能会阻塞DOM的渲染
2. 与页面加载事件的关系
- load
当页面的html、css、js、图片等资源都已经加载完之后才会触发 load 事件
- DOMContentLoaded
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。
区别:
“async可能会在 DOMContentLoaded之前或之后,保证在页面的 load 事件之前执行。”
”defer保证在触发DOMContentLoaded之前执行,执行完毕后触发DOMContentLoaded事件。“
3. 多个script标签下的执行顺序
- 普通的script标签,HTML5 规范要求脚本应该按照它们出现的顺序执行。
- 对于async属性的script标签,执行的顺序不确定,先下载完的脚本先执行
- 对于defer属性的script标签,脚本执行的顺序与下载的完成时间无关,按照script标签位置顺序执行
三、使用场景
- async
主要是不涉及操作DOM的事件
- defer
操作DOM的脚本,为防止元素尚未加载完成,脚本找不到元素报错。