script标签中的async,defer属性
script标签中的async,defer属性一、有无async,defer属性的含义
1、<script src="script.js"></script>
没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
2、<script async src="script.js"></script>
(1)、有 async,浏览器会立即下载脚本,但不妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。加载和渲染后续文档元素的过程和script.js的加载与执行并行进行(异步)。
(2)、async不保证按照脚本出现的先后顺序执行,因此,确保两者之前互不依赖非常重要,指定async属性的目的是不让页面等待两个脚本的下载和执行,从而异步加载页面其他内容,建议异步脚本不要在加载期间修改DOM。
(3)、异步脚本一定会在页面的load事件前执行,但可能会在DOMContentLoaded事件触发之前或之后执行。
3、<script defer src="script.js"></script>
有 defer, 表示脚本会被延迟到文档完全被解析和显示之后再执行,加载后续文档元素的过程将和script.js的加载并行进行(异步)。
二、浏览器的在遇到defer和async属性的<script>的执行过程如下
1、WEB浏览器创建Document对象,并且开始解析WEB页面,解析HTML元素和它们的文本内容后添加Element对象和Text节点到文档中。这个过程的readystate的属性值是“loading”
2、当HTML解析器遇到没有async和defer属性的<script>时,它把这些元素添加到文档中,然后执行行内或外部脚本。这些脚本会同步执行,并且在脚本下载(如果需要)和执行解析器会暂停。这样脚本就可以用document.write()来把文本插入到输入流中。解析器恢复时这些文本会成为文档的一部分。同步脚本经常单定义函数和注册后面使用的注册事件处理程序,但它们可以遍历和操作文档树,因为在它们执行时已经存在了。这样同步脚本可以看到他自己的<script>元素和它们之前的文档内容
3、当解析器遇到了设置async属性的<script>元素时,它开始下载脚本,并继续解析文档。脚本会在它下载完成后尽快执行,但是解析器没有停下来等他下载。异步脚本禁止document.write()方法。它们可以看到自己的<script>元素和它之前的所有文档元素,并且可能或干脆不可能访问其他的文档内容。
4、当文档完成解析,document.readyState属性变成“interactive”。
5、所有有defer属性的脚本,会被它们在文档的里的出现顺序执行。异步脚本可能也会在这个时间执行。延迟脚本能访问完整的文档树,禁止使用document.write()方法。
6、浏览器在Document对象上触发DOMContentLoaded事件。这标志着程序执行从同步脚本执行阶段转到异步事件驱动阶段。但要注意,这时可能还有异步脚本没有执行完成。
7、这时,文档已经完全解析完成,但是浏览器可能还在等待其他内容载入,如图片。当所有这些内容完成载入时,并且所有异步脚本完成载入和执行,document.readyState属性变为“complete”,WEB浏览器出发Window对象上的load事件。
8.从此刻起,会调用异步事件,以异步响应用户输入事件,网络事件,计算器过期等。
三、defer和async的比较
1、相同点
(1)、加载文件时不阻塞页面渲染;
(2)、对于inline的script无效;
(3)、使用这两个属性的脚本中不能调用document.write方法;
(4)、有脚本的onload的事件回调;
(5)、允许不定义属性值,仅仅使用属性名;
2、不同点
(1)、每一个async属性的脚本都在它下载结束之后立刻执行,同时会在window的load事件之前执行。所以就有可能出现脚本执行顺序被打乱的情况;
(2)、每一个defer属性的脚本都是在页面解析完毕之后,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行。