前端之路——浏览器是如何「画」出页面的
浏览器是如何渲染页面的
关于浏览器渲染页面,不得不提的就是“回流”和“重绘”。
浏览器读取一个html文件后,会自上而下,一个一个地解析其中的标签,生成相应的DOM树,这是浏览器理解文档的方式。HTML标签中,除了基础的语义化标签之外,还有其他功能标签。这其中有两个特殊的标签:
- <link>标签,可以用来引入其他文档,例如CSS文件
- <script>标签,可以用来引入脚本文档,例如JavaScript
<link>标签和CSS
浏览器在读取了<link>标签后,会去下载相应的CSS文件,然后会把CSS文件的内容解析成CSS规则树(CSS Rule Tree),接下来整合DOM和CSS规则树的内容,确认DOM树中每个节点的位置及其样式,生成渲染树(Render Tree),最后浏览器会根据渲染树“画”出页面。在这个过程中,就发生了回流和重绘。
所谓回流,简单理解就是确定每个节点的相互关系,最主要的是位置关系、显示隐藏。
所谓重绘,简单理解就是画出每个节点的真实样子。
从这个角度更容易理解为什么回流消耗的资源更多,而重绘消耗的资源更少,因为重绘只是单个节点“自己的事”,回流则会影响到“别人”。也正因为如此,我们需要减少回流和重绘。
对于静态页面,一旦加载完毕,就不会发生变化,即不会发生回流和重绘,需要优化的是第一次加载是时的情况。
针对CSS文件,因为解析完毕后会“合成”渲染树,所以需要尽可能在浏览器解析完标签生成DOM树时,也能生成CSS规则树,这样浏览器根据渲染树渲染出来的页面就是最终的样子,这是最理想的结果。这也就是为什么一般我们会把<link>标签放在HTML文件的头部。然而,浏览器解析CSS生成CSS规则树的同时会阻塞DOM树的生成,进而导致页面“白屏”。基于这种考虑,应该把重要的、可能会导致元素位置等导致回流的CSS的规则所在的文件放在页面头部,其他仅仅是调整颜色、背景等CSS放到页面底部处理。
<script>标签和JS
类似地,浏览器在读取到<script>标签后,也会去下载相应的JS文件,下载完成后就会解析并执行,因此会阻塞浏览器的渲染页面。为了避免影响页面渲染工作,可以使用<script>标签的defer或者async属性。
- defer
defer会使浏览器异步去下载JS文件,并且在页面渲染完成之后才会执行。多个带defer的<script>标签会按照文件顺序依次执行。 - async
async同样会使浏览器异步下载JS文件,并且异步去执行JS文件。多个带async的<script>标签之间的执行都是异步的,不能保证先后顺序