浏览器渲染
渲染过程中,如果遇到script标签就停止渲染,执行 JS 代码。 因为浏览器有GUI渲染线程与JS引擎线程,为了防止渲染出现不可预期的结果,这两个线程是互斥的关系。
JavaScript的加载、解析与执行会阻塞DOM的构建,也就是说,在构建DOM时,HTML解析器若遇到了JavaScript,那么它会暂停构建DOM,将控制权移交给JavaScript引擎,等JavaScript引擎运行完毕,浏览器再从中断的地方恢复DOM构建。
也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer(延迟) 或者 async(异步) 属性(下文会介绍这两者的区别)。
JS文件不只是阻塞DOM的构建,它会导致CSSOM也阻塞DOM的构建。 原本DOM和CSSOM的构建是互不影响,井水不犯河水,但是一旦引入了JavaScript,CSSOM也开始阻塞DOM的构建,只有CSSOM构建完毕后,DOM再恢复DOM构建。
这是因为JavaScript不只是可以改DOM,它还可以更改样式,也就是它可以更改CSSOM。因为不完整的CSSOM是无法使用的,如果JavaScript想访问CSSOM并更改它,那么在执行JavaScript时,必须要能拿到完整的CSSOM。所以就导致了一个现象,如果浏览器尚未完成CSSOM的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟脚本执行和DOM构建,直至其完成CSSOM的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后在继续构建DOM。
async和defer
async: 异步加载下载js脚本文件,在加载下载完之后立马执行js脚本文件.多个async脚本文件执行时执行顺序没有保障.执行js过程中,会阻塞html的解析和渲染
defer: 异步加载下载js脚本文件,在加载下载完之后等到html解析完毕才执行js脚本文件.多个defer脚本文件执行时的执行顺序有保障.执行js过程中,会阻塞html的解析和渲染
async和defer的共同作用:
- 在加载和下载js的时候不会阻塞html的解析和渲染
二者的区别
- 执行的时间不同,async在下载或加载完成之后直接执行js脚本文件,但是defer会等到html完全的解析之后才会执行相对应的js文件. (注意点:在js文件执行的时候会阻塞html的解析和渲染) )
- 如果存在多个async和defer的时候,defer的执行顺序是有保障的,async的执行顺序是没保障的.async的执行顺序是谁先加载或下载完毕谁先执行.不讲求先来后到的规矩.但是defer不同,它还是讲先来后到的关系.
补充:如果想要使用DOM最好使用defer,因为它在html解析完全之后才执行.而async的执行时间没有保障.它可能在html解析完全之前就执行了.不能获取到所有的DOM