默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本(如果是用src引用的外联脚本文件,遇到<script>标签会开始下载文件然后执行),再继续向下渲染。
如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应。这显然是很不好的体验,所以浏览器允许脚本异步加载,<script>标签提供的async属性与defer属性可以实现脚本文件的异步加载:
1. defer属性
表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外本脚本文件有效。
浏览器的解析顺序:
- 浏览器开始解析 HTML 网页。
- 解析过程中,发现带有defer属性的<script>元素。
- 浏览器继续往下解析 HTML 网页,同时并行下载<script>元素加载的外部脚本。
- 浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本。
2. async属性
表示应该立即下载脚本,但不应该妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本。只对外部文件有效。
浏览器的解析顺序:
- 浏览器开始解析 HTML 网页。
- 解析过程中,发现带有async属性的script标签。
- 浏览器继续往下解析 HTML 网页,同时并行下载<script>标签中的外部脚本。
- 脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本。脚本执行完毕,浏览器恢复解析 HTML 网页。
需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。
3. defer与async的区别
- defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
- async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
- 一句话,defer是“渲染完再执行”,async是“下载完就执行”。
- 如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。