学习到了页面渲染的部分,很好奇到底发生了什么,于是区网络上搜索学习了一番,现在来做一个小总结。
第一步 解析HTML文件 构建DOM树
首先,在浏览器的到信息的第一时间,就可以开始解析了。
“解析”是浏览器将通过网络接收的数据转换为 DOM 和 CSSOM 的步骤,通过渲染器把 DOM 和 CSSOM 在屏幕上绘制成页面。
而 DOM 就是 文档物件模型(Document Object Model, DOM) 将整个html文档定义成「文档对象模型」详细的结构,并提供完整的API 给编程语言像js来操作。
而 CSSOM 就是 CSS 对象模型 (# CSS Object Model)* 是树形形式的所有CSS选择器和每个选择器的相关属性的映射,具有树的根节点,同级,后代,子级和其他关系。
在渲染到屏幕上面之前,HTML、CSS、JavaScript 必须被解析完成。
每一个HTML标签都有一个对应的节点,并且每一个文本也都会有一个对应的文本节点。DOM树的根节点就是document.documentElement,对应的是HTML标签。树反映了不同标签之间的关系和层次结构。嵌套在其他标签中的标签是子节点。DOM 节点的数量越多,构建 DOM 树所需的时间就越长。
当解析器解析资源的时候,若遇到<script>文件时,通常情况下便会发生阻塞解析等到加载执行完里面的JS文件才会继续解析接下来的资源。这样非常地耗费时间。所以这里可以让<script>放置在页面的最后,或是使用无顺序的async属性来进行异步加载解析。也就是说,不用等待JS解析执行完毕,其他元素的解析也在同时进行。但坏处是async不保证顺序,不保证时机。谁先执行完成谁输出,这样很容易造成需要其操纵的DOM还没有加载解析完成,就执行了JS,会报错。所以更推荐使用defer属性来完成。defer会对于多个defer外置的js按前后顺序执行。和正常情况下一样,但是不会阻塞DOM解析。
<script async src="js路径"></script>
<script defer src="js路径"></script>
这时候浏览器的预加载扫描器也会帮忙。浏览器构建 DOM 树时,这个过程占用了主线程。当这种情况发生时,预加载扫描仪将解析可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体。
CSS的加载会阻塞JavaScript,因为 JavaScript 经常用于查询元素的 CSS 属性。
第三步 解析CSS 并构建CSSOM树
CSS 对象模型和 DOM 是相似的。DOM 和 CSSOM 是两棵树。解析出CSS样式,构建出CSSOM树。
解析CSS的时候会按照如下顺序来定义优先级:
浏览器默认设置 < 用户设置 < 选择器设置样式 < 内联样式 <!imprtant< html中的style。
这个时候会发生阻塞渲染。
阻塞渲染 :阻塞点后面的标签会继续被正常解析,DOM也会正常被看见。img,link等会继续发送请求获取外部资源,但不会触发页面渲染(没有样式,白屏),也不会执行JavaScript代码。
若我们把link样式表放在了head标签里,当CSS加载时间过长的时候,就会发生白屏现象。打开网页,页面一片空白,过了一段时间突然展示完整的有样式的内容。就是因为CSS还没有加载完成。
若我们把样式表放在了页面底部,当CSS加载时间过长的时候,则会出现FOUC(Flash Of Unstyled Content 文档样式闪烁,暂时失效) 现象。也就是页面一开始展示无样式的内容,过了一段时候突然样式变正常。因为DOM结构已经完全加载完毕了,但是CSS并没有加载完毕。会让页面上的元素呈现默认样式。
注意:
尽量把link放在head标签内,防止出现FOUC。
若必须将样式表放在其他位置,则可以在页面顶端放置一些默认样式:
- 例如让所有内容都不显示;
- 或者显示一个loading的界面。
第三步 将CSS与DOM合并,构建渲染树(render tree)
在解析步骤中创建的 CSSOM 树和 DOM 树组合成一个 Render 树,从 DOM 树的根开始构建,遍历每个可见节点。
DOM树完全和HTML标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如head、display:none的元素等。具有 visibility: hidden 的节点会出现在 Render 树上,因为它们会占用空间。
渲染树里每一个节点都有CSS对应的属性。且一大段文本中的每一个行在渲染树中都是独立的一个节点。
第四步 在渲染树的基础上进行布局,计算每个节点的几何结构
在渲染树上运行布局以计算每个节点的几何体。布局是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。CSS和JavaScript往往会多次修改DOM或者CSSOM。所以在这里我们会遇到reflow和repaint。
Reflow(回流)Relayout(重新布局) 重新计算元素的几何尺寸,位置。 假设通过JS或是别的什么方法把一些元素消除了,浏览器又要重新计算每个元素的位置,渲染树,几何结构。
Repaint(重绘) 重新绘制界面发生变化的部分。 通过JS修改元素的颜色,其他元素的几何结构位置没变。 只是要改变样式颜色。重绘。
要尽量减少这两个东西的发生。尽量一次性修改样式,给动画使用绝对定位可以减少reflow次数,DOM离线后修改。都是可行的办法。
第五步 把每个节点绘制到屏幕上(painting)
最后一步是将各个节点绘制到屏幕上。绘制可以将布局树中的元素分解为多个层。将内容提升到 GPU 上的层(而不是 CPU 上的主线程)可以提高绘制和重新绘制性能。每个层绘制完成之后,还要将他们合成到一起。