在网站页面中,通常需要引入外部js资源,然而外部的js资源可能导致DOM阻塞,影响页面加载速度。通过异步或者延迟执行js,可以做到引用外部js资源而不阻塞DOM的目的。用法是直接在script标签中使用defer和async,那么它们两个有什么区别呢?
从字面上来理解,defer是延迟,而async则是异步(Asynchronous)。w3c上这样解释defer和async:defer属性是一个布尔值的属性,当存在这个属性时,它指定的脚本将在页面解析之后执行script标签的讲解。
概念的东西让人很难琢磨,下面是我对使用不同属性的外部引用标签的一个实验, 用php模拟一个外部javascript脚本:
header("Content-Type:text/javascript;charset=utf-8;");
sleep(2);
?>
console.log("This is the Script");
前台页面代码如下:
This is The Document.
下面的两张图片分别是使用defer和async的时间线:

使用defer的时间线

使用async的时间线
注意那条蓝色的时间线是DOMContentloaded时间,可以看到使用defer的DOMContentloaded事件是在JS脚本执行完之后,而使用async则是在html文档被解析完之后,相同之处是打印出”This is the Script”的时间一样。为什么会这样呢?
我们知道,如果javascript引入脚本既不使用defer也不使用async,整个页面是必须在引入脚本下载执行完毕之后才能继续被加载。而根据前面的概念,defer是延迟执行,async是异步执行。它们之间最大的区别是defer的执行时间是html文档被解析完成之后,而async的执行时间是不确定的,只要脚本被下载完毕,就会执行;它们之间的相同点是不会阻塞其它元素的加载。如果一个页面中有多个使用defer属性的外部脚本, 他们会按照顺序依次执行。
defer和async的应用
由于defer会在页面解析完成之后执行,所以当js脚本运行的时候,页面已经是解析完毕了的状态,这时候已经可以进行DOM操作, 而如果不使用defer,文档的状态不确定,所以不能进行操作。如上面的代码中,如果把js.php改成如下代码,打开前面页面,发现内容会被更改:
header("Content-Type:text/javascript;charset=utf-8;");
sleep(2);
?>
document.body.innerHTML="Body Content Has Been changed";
使用defer需要注意的是,由于defer的执行时机是页面解析完之后,含有defer的外部脚本执行时间是优先于内嵌脚本的,如下,内嵌脚本中放入一个DOMContentloaded的事件监听:
document.addEventListener("DOMContentLoaded",function(){
console.log("内部脚本,DOMContentloaded发生!");
},false);
>
This is The Document.
发现外部脚本内容先被打印出来。
而对于使用async的外部脚本来说,是在javascript文件被完全加载之后马上执行, 所以它可能会在DOM加载好之前被执行,也可能会在之后执行。这里的问题就是,如果异步加载的JS在DOM准备好之后执行,那么操作DOM的动作势必执行不了,如果在DOM准备好之前执行,则无法监听DOMContentloaded事件, 而依赖DOMContentloaded的事件回调就无法启动。例如一个HTML页面加入如下异步脚本代码:
header("Content-Type:text/javascript");
sleep(5);
?>
document.addEventListener("load",function(){
console.log('DOMContentLoaded Event');
},false);
发现”DOMContentLoaded Event”无法打印,而去掉async属性后,就会正常打印”DOMContentLoaded Event”, 这说明在异步加载后,外部脚本执行的时候,DOMContentLoaded事件已经过去。
综上,defer和async最大的区别是执行时间的不同,defer实在DOM解析完毕之后马上执行,而async是在脚本下载完毕之后马上执行。执行时机的不同造成最终的效果各不相同,在实际中要小心对待。
参考资料
本文详细探讨了在HTML中使用defer和async属性加载外部JavaScript脚本时的区别。defer属性确保脚本在DOM解析完成后执行,且按顺序执行多个脚本,而async属性则在脚本下载完成后立即执行,执行顺序不确定。因此,defer适合不依赖DOM的内容,而async适用于可以异步执行且不影响页面流程的脚本。在实际应用中,理解这两者的差异对于优化网页加载性能至关重要。
141

被折叠的 条评论
为什么被折叠?



