defer和async是什么?
将JavaScript 插入HTML的主要方法是使用 <script>
元素。 <script>
元素包含了8个属性,其中包括defer和async。
在《javascript高级程序设计(第4版)》中对这两个属性的描述是这样的:
async:可选。表示应该立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。只对外部脚本文件有效。
defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。在IE7及更早的版本中,对行内脚本也可以指定这个属性。
用一张图来说明:
两者都是用来控制<script>
资源的载入以及执行顺序。有defer
属性的脚本会阻止DOMContentLoaded事件,直到脚本被加载且解析完成。
产生原因
那什么是DOMContentLoaded?当一个HTML文档被加载和解析完成后,DOMContentLoaded事件便会被触发。HTML文档被加载和解析完成后,会产生DOM(文档对象模型);如果有CSS则会生成CSSOM(CSS 对象模型),然后再由 DOM 和 CSSOM 合并产生渲染树。浏览器根据渲染树,知道了所有节点的位置布局以及样式,从而进行绘制。
但Javascript可以堵塞DOM生成。传统写法中,当浏览器读到 <script>
时,便会暂停解析DOM,转而去下载 <script>
中的资源,并在下载完成后立刻执行。而这可能会导致操作DOM节点的错误;或是由于 <script>
资源得下载执行,导致画面暂时空白,产生不良的用户体验。
另外,因为 JavaScript 可以查询任意对象的样式,所以意味着在 CSS 解析完成,也就是 CSSOM 生成之后,JavaScript 才可以被执行。
虽然可以通过将<script>
放到<body>
前面解决,但由于同步原因,DOM都生成了才开始下载<script>
的资源,也会造成用户的不良体验。而defer和async通过异步的方式解决了上述问题。
使用场景
1.defer
如果 <script>
标签中包含defer
,那么这一脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在defer
脚本执行结束后才会被触发。对于不会生成任何网页内容的脚本,可以使用defer。
2.async
如果存在async
属性,则脚本将会在可用时立即异步执行。与defer
的相同点是也会在后台执行下载,但不同的是当下载完成会马上暂停 DOM 解析(如果还没有解析完成的话),并开始执行 JavaScript。因此无法保证脚本的执行顺序。一般用于独立的小模块中,例如背景Logo、页面广告等,在避免造成使用者体验变差的同时,尽量早的产生效果。