前置概念
在浏览器加载HTML过程中,HTML 解析器运行于主线程之中,并且在遇到 </script> 标签时会暂停 DOM 的解析与渲染,直到脚本中的内容被解析执行完成,才能继续解析渲染 DOM,这样也被称为“渲染阻塞”。
举个例子:
当我们写了这样一段代码后
当 HTML 解析器遇到
</script> 时,会去解析并执行</script>中的 index.js,这时 DOM 解析就会暂停,直观感受就是页面长时间的空白。
观察 chrome 性能面板可以直观看到这一现象。
通过时间线可以看到 script 脚本的执行会暂停 Parse HTML,在脚本执行完成之后继续执行了 这就是我们所说的渲染阻塞。
async
MDN说明:
对于普通脚本,如果存在
async属性,那么普通脚本会被并行请求,并尽快解析和执行。该属性能够消除解析阻塞的 Javascript。
翻译一下:如果给 script 标签加上 async 属性,script 脚本中的内容会被异步加载,并且在加载完成之后会尽快(立即?)执行。
举个例子:
我们给</script>加上了 async 属性
再观察下 chrome 性能面板
可以看到,在脚本执行完成之后依然进行了 Parse HTML,这里会出现两种情况,如果 DOM 结构比较简单,内容比较少,在异步加载完脚完成本后 ,DOM 的解析已经完成,就不会再触发 Parse HTML,如果未完成会在异步脚本执行完成后继续解析。
defer
MDN说明:
这个布尔属性被设定用来通知浏览器该脚本将在文档完成解析后,触发
DOMContentLoaded (en-US)事件前执行。有defer属性的脚本会阻止DOMContentLoaded事件,直到脚本被加载并且解析完成。
翻译一下:如果给 script 标签加上 defer 属性,script 脚本中的内容会被异步加载,并且在加载完成之后,如果 DOM 还未解析完成,会暂时挂起,等 DOM 解析完成后在执行。
举个例子:
我们给</script>加上了 defer 属性
观察下 chrome 性能面板
可以看到
</script>中的 index.js 的执行会在 DOM 解析完成后才开始执行。
结论:
async 属性虽然解析过程不会阻塞 DOM,但是解析完成后会很快执行,执行过程依然会阻塞 DOM
defer 属性会等到 DOM 解析完成后执行脚本,不会阻塞 DOM 的解析过程