浏览器的渲染机制

142 阅读4分钟

1 浏览器首次渲染(六步)

  • 解析HTML,构建HTML树(DOM文档对象模型)
  • 解析CSS,构建CSS树(CSSOM)
  • 将两棵树合并成一颗渲染树(render tree) image.png
  • 根据渲染树对每个节点进行layout布局(根据文档流定位、盒模型、计算大小和位置)
  • 把节点Paint绘制颜色(把边框颜色、文字颜色、阴影等画出来)
  • Composite合成拍成一层(根据层叠关系展示画面),实现最终渲染。

2 再次渲染

操作JS改变CSS属性会触发不同的渲染流程来更新样式:

  • 点击按钮,删除DOM里的元素,元素从文档流里消失,则需要重新构建DOM树,和首次渲染一样。
  • 改变布局、滚动页面:layout、repaint、composite。需要重新计算元素位置
  • 改变背景颜色:repaint、composite,不用layout,没有改变布局。
  • transform变换:只合成Composite,布局和背景颜色都没有改变。

Reflow 和 Repaint

Reflow 回流:重新计算元素的几何尺寸大小,位置,内容

Repaint 重绘:绘制界面发生变化的部分,颜色,背景色

案例(repaint一定会发生)

  • 添加、删除、更新DOM节点(reflow、repaint)
  • 修改元素的magin、padding,border(reflow、repaint)
  • display:none(reflow、repaint)元素从文档流中消失
  • visibility:hidden(repaint)元素仍占据空间
  • 修改颜色、背景色(repaint)

回流必将引起重绘,重绘不一定会引起回流。

如何优化

JS

  • 尽量一次性修改样式(JS每次单独执行一次,改变一个样式,就要重绘回流一次)
  • DOM离线后修改。(对虚拟DOM不断修改,再一次性渲染到真实DOM上)

CSS

  • 动画使用绝对定位可以减小reflow(浏览器只会计算这个元素本身位置的变化,小范围计算)
  • 使用transform:translate,触发浏览器的GPU进行渲染,计算绘制速度更快,减少CPU的负荷。

3 CSS 和 JS 对页面的阻塞

浏览器下载解析 HTML,构建 DOM 树,下载解析CSS,构建CSSOM树,这两个步骤可以是并行的,之后 DOM 树和 CSSOM树结合成 Render Tree。我们一般在HTML里是这样引入CSS和JS的:

<head>
  <link rel="stylesheet" href="http://a.com/css" />
</head>
<body>
  ...
   <script>...</script>
</body>

当CSS和JS的解析时间很久,会引发一些问题:

  • 阻塞解析:当前阻塞点后面的标签不会被解析。
  • 阻塞渲染:阻塞点后面的标签会继续被解析,但不会触发页面渲染(白屏),阻塞这之后的JS代码执行。

当CSS加载时间很久,link标签放置位置不同,会引发以下两种情况之一:

  • 白屏:页面一片空白,突然展示完整的有样式的内容。(当link放在head内)
  • 无样式内容闪烁FOUC:页面一开始展示无样式的内容,突然样式正常。(link放在页面底部)

- CSS加载:不阻塞解析,会阻塞渲染

  1. 不阻塞 DOM 树的解析,但会阻塞CSSOM的解析,进而阻塞 Render Tree (因为渲染树需要等待CSSOM,减少不必要的回流重绘)
  1. 阻塞在 CSS 之后的 JS 的解析执行(因为某些 JS API 如 getComputedStyle 需要最新的样式数据)

因此,尽量把link放在head标签内,防止出现FOUC

- JS加载:阻塞解析

JS 会阻塞在其后的 DOM 树的构建,进而影响 Render Tree。在JS运行请求完成之后,DOM节点才会被解析。

因此,尽量把外置JS放置在body的最后,以便让DOM尽快展现

例子:以下内容是不能成功显示的,因为浏览器解析时遇到script标签执行,此刻没有执行解析到body内的标签,当前页面上什么都没有,是无法获取到这个标签,因此应该将<script>标签放到<p>后面。

<head>
  <link rel="stylesheet" href="http://a.com/css" />
  <script>
    document.querySelector('p').innerText="hhh"
  </script>
</head>
<body>
  <p>hello</p>
</body>

总结

  • CSS不会阻塞DOM解析,但是会阻塞DOM渲染,CSS会阻塞render tree的生成,进而阻塞DOM的渲染。
  • CSS会阻塞JS的执行
  • JS会阻塞DOM解析
  • 浏览器遇到<script>标签且没有deferasync属性时会触发页面渲染
  • <link>最好放在<head>里,<script>最好放在<body>最底部

常见问题

1.让一个元素消失的办法

  • display:none; 元素会从页面中彻底消失,它原本占据的空间会被其他元素占有,会造成浏览器的回流与重绘。
  • visibility: hidden;元素会从页面中消失,它原本占据的空间会被保留,会造成浏览器的重绘,适用于希望元素隐藏又不影响页面布局的场景,不再响应绑定的事件。
  • opacity:0;元素变成透明的,但原本占据的空间还在,仍响应绑定的事件。
  • transform: scale(0,0) 同上,视觉缩放。
  • 设置盒模型各属性为0,使用overflow: hidden;来隐藏子元素。
.box1 {
        width: 0;
        height: 0;
        padding: 0;
        border: 0;
        margin: 0;
        overflow: hidden;
}
  • 移出屏幕:设置元素绝对定位与负值的top、left等将元素移出屏幕。
  • 设置绝对定位z-index,将z-index:-999。也要同时设置其他元素的z-index值
.box1 {
        position: absolute;
        z-index: -999;
}
.box2 {
        position: absolute;
        z-index: 1;
}