普通的dom元素
当浏览器请求到了html文件之后会从上到下一行一行的解析html,当他遇到普通的标签例如div、span等这些会解析为dom树。dom树并不是我们在页面看到的东西,页面上看到的是所谓的渲染树。dom树是用来操作的dom节点的。
外链的link元素
当解析过程中遇到link标签时,会立即发起下载的请求(下载过程和解析dom是并行的),等下载完成后开始解析文件的内容并且生成cssom树。当然style不需要下载直接解析并且生成cssom树。
然后就会根据dom树和cssom树生成render tree,所以到目前为止css的下载不会阻塞dom的解析,但是css的下载会阻塞render tree的生成,也就是会间接阻塞页面渲染。
img等媒体元素
如果在解析过程遇到了img或者audio这种标签,会立即发起下载请求(下载请求和解析dom是并行的不会阻塞html的解析),等待资源下载完成浏览器会将资源渲染到对应的位置。而在这个过程中如果你的img标签没有给定一个宽度和高度那么初始渲染的宽高就是0,等待资源加载完成之后宽度和高度会再被内容撑开,由于宽高变化所以会触发重排和重绘,来更新布局。
script元素
如果解析过程遇到了'<script>...</script>'标签则会停止解析html,会去一行一行的执行script里面的代码,在这段script执行完毕之前,html的解析会一直处于暂停状态。如果遇到'<script src='xxx.js'></script>'引用的外部脚本,浏览器会立即发起请求,去下载这个文件这个下载的过程是同步的,也就是在浏览器会等脚本下载完,再去执行脚本,等脚本执行完,再去继续解析html。这是为什么那?因为在脚本中是可能会操作cssom或者注册dom事件,或者删除dom等操作的。所以需要阻塞html的解析与渲染。而这种行为由于会阻塞hmtl的解析,所以就会导致一些img或者link下载的时间向后推迟,所以我们最好把script脚本放在html的最后来加载。
defer(推迟)、async(异步)的script
这两种方式都是会异步下载js文件,但是执行的时机不一样,async的脚本会等脚本下载完成后立即执行,也就是说脚本下载的快的话,可能会在html解析的过程中就下载完了,然后立即开始执行js,那这种情况就可能会阻塞html的继续解析(多个async属性的script,谁先现在完谁先执行)。 defer的话是等dom解析完成后执行,也就是说defer肯定不会阻塞dom的解析(多个defer的话,会按照文档中的顺序一个接一个执行)。 无论async还是defer都会在DOMContentLoaded事件之前执行。
DOMContendLoaded
MDN文档 developer.mozilla.org/zh-CN/docs/…
DOMContendLoaded 发生在页面渲染之前,这一阶段标志着cssom和dom是都ok的状态,在这一阶段我们可以访问到dom节点。但是img和audio这种可能还没加载出来。
load MDN文档 developer.mozilla.org/zh-CN/docs/… load事件里可以访问到img渲染的宽高。