script元素和页面解析的关系
script元素的处理
- 浏览器在解析HTML的过程中,遇到了script元素是不能继续构建DOM树的
- 它会停止继续构建,首先下载js代码,并且执行js脚本
- 只有等到js脚本执行结束后,才会继续解析HTML,构建DOM树
为什么要这样做呢?
- 因为js的作用之一就是操作dom,并且可以修改dom
- 如果我们等得到dom树构建完成并且渲染再执行js,会造成严重的回流和重绘,影响页面的性能
- 所以再遇到script元素时,优先下载和执行js代码,再继续构建dom树
但是这个往往也会带来新的问题,特别是现代页面开发中:
- 在目前的开发模式中(比如Vue,React),脚本往往比HTML页面更“重”,处理时间需要更长
- 所以会造成页面的解析阻塞,在脚本下载、执行完成之前,用户在页面上什么都看不到
为了解决这个问题,script元素给我们提供了两个属性(attribute):defer和async
defer属性
defer属性告诉浏览器不要等待脚本下载,而继续解析html,构建dom tree
脚本会由浏览器来进行下载,但是不会阻塞dom tree的构建过程
如果脚本提前下载好了,它会等待dom tree构建完成,在DOMContentLoaded事件之前先执行defer中的代码
在defer代码中,dom tree已经构建完成了,可以操作dom
所以DOMConteneLoaded总是会等待defer中的代码先执行完成
另外多个带defer的脚本是可以保持正确的顺序执行的
从某种角度来说,defer可以提高页面的性能,并且推荐放到head元素中
- 这样可以让浏览器提前去下载,而不是等到都加载完了然后再去下载
注意:defer仅适用于外部脚本,对于script默认内容会被忽略
async属性
async特性与defer有些类似,它也能够让脚本不阻塞页面
async是让一个脚本完全独立的:
- 浏览器不会因async脚本而阻塞(与defer类似)
- async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本
- async不能保证在DOMContentLoaded之前或之后执行
- 它是下载一旦好了就执行,不能确定脚本运行顺序
特点:
- 比较危险,不能在里面随便操作DOM,因为不知道有没有构建好
总结defer和async
defer通常用于需要在文档解析后操作DOM的js代码,并且对多个script文件有顺序要求的
async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的
