script标签的defer属性和async属性
阮一峰大神的javascript标准参考教程--script元素教程地址
- 作用:解决脚本下载阻塞网页渲染的问题
场景
script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了。
直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染。
现在大家习惯于在页面中引用各种的第三方脚本,如果第三方服务商出现了一些小问题,比如延迟之类的,就会使得页面白屏。
好在script提供了两种方式来解决上述问题,async和defer,这两个属性使得script都不会阻塞DOM的渲染。
但既然会存在两个属性,那么就说明,这两个属性之间肯定是有差异的。
defer属性
<script src="a.js" defer></script>
<script src="b.js" defer></script>
加了defer属性script标签的页面,运行流程如下:
1.浏览器开始解析HTML页面
2.遇到有defer属性的script标签,浏览器继续往下面解析页面,且会并行下载script标签的外部js文件
3.解析完HTML页面,再执行刚下载的js脚本(defer脚本会在文档渲染完毕后,DOMContentLoaded事件调用前执行,即刚刚解析完html,且可保证执行顺序就是他们在页面上的先后顺序)
若有不了解DOMContentLoaded事件,可以看下文的基础知识点[DOMContentLoaded事件]
注意事项:
- 1.内置js代码的script标签,以及动态生成的script标签,defer属性不生效
- 2.有defer属性的script标签脚本文件里不能使用document.write方法
- 3.defer脚本会在DOMContentLoaded和load事件之前执行。
- 4.defer只适用于外联脚本,如果script标签没有指定src属性,只是内联脚本,不要使用defer 如果有多个声明了defer的脚本,则会按顺序下载和执行
async属性
<script src="a.js" async></script>
<script src="b.js" async></script>
流程如下:
1.浏览器开始解析页面
2.遇到有async属性的script标签,会继续往下解析,并且同时另开进程下载脚本
3.脚本下载完毕,浏览器停止解析,开始执行脚本,执行完毕后继续往下解析
注意事项:
- 1.无法保证脚本的执行顺序,哪个脚本先下载完毕,就先执行哪个。\
- 2.也不能使用document.write方法。
- 3.
async脚本会在加载完毕后执行,async脚本的加载不计入DOMContentLoaded事件统计,async会在load事件之前执行,但并不能确保与DOMContentLoaded的执行先后顺序。 - 4.只适用于外联脚本,这一点和defer一致,如果有多个声明了async的脚本,其下载和执行也是异步的,不能确保彼此的先后顺序。
两者区别
使用场景区分:
1.脚本之间没有依赖关系的,使用async
2.脚本之间有依赖关系的,使用defer
3.若同时使用async和defer,defer不起作用,async生效
推荐的应用场景
defer
如果你的脚本代码依赖于页面中的DOM元素(文档是否解析完毕),或者被其他脚本文件依赖。
例:
- 评论框
- 代码语法高亮
polyfill.js
async
如果你的脚本并不关心页面中的DOM元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据。
例:
- 百度统计
如果不太能确定的话,用defer总是会比async稳定。。
具备的基础知识点
那么我们是不是要了解一下普通script运行原理:
普通script
文档解析的过程中,如果遇到script脚本,就会停止页面的解析进行下载(但是Chrome会做一个优化,如果遇到script脚本,会快速的查看后边有没有需要下载其他资源的,如果有的话,会先下载那些资源,然后再进行下载script所对应的资源,这样能够节省一部分下载的时间 Update: 2018-08-17)。
资源的下载是在解析过程中进行的,虽说script1脚本会很快的加载完毕,但是他前边的script2并没有加载&执行,所以他只能处于一个挂起的状态,等待script2执行完毕后再执行。
当这两个脚本都执行完毕后,才会继续解析页面。
缺点:
- 如果外部脚本加载时间很长(一直无法完成下载),那么浏览器就会一直等待脚本下载完成,造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”
- 如果浏览器会同时并行下载
a.js和b.js,但是,执行时会保证先执行a.js,然后再执行b.js,即使后者先下载完成,也是如此。也就是说,脚本的执行顺序由它们在页面中的出现顺序决定,这是为了保证脚本之间的依赖关系不受到破坏。当然,加载这两个脚本都会产生“阻塞效应”,必须等到它们都加载完成,浏览器才会继续页面渲染。
DOMContentLoaded 事件
概念(来自MDN):当初始HTML文档已完全加载和解析时,将触发DOMContentLoaded事件,而不需要等待样式表,图像和子框架页面加载(事件可以用来检测HTML页面是否完全加载完毕(fully-loaded))。
DOMContentLoaded 事件发生在 docuemnt 对象上,日常我们使用 addEventListener 绑定它。
document.addEventListener('DOMContentLoaded', ready);
DOMContentLoaded 事件是在页面的 DOM 树结构构建完毕后触发的,如果我们不需要等到 HTML 的外部资源(图片、样式表、脚本等)完全加载,就希望操作页面元素,那么就可以使用它,一般来说,比 window.onload 事件要快。