← 返回首页
浏览器渲染原理
发表时间:2023-02-19 18:46:30
浏览器渲染原理

深入理解浏览器的渲染原理。

1.浏览器渲染原理概述

浏览器渲染的本质就是把要解析的HTML字符串转换为屏幕上要显示的像素信息。

浏览器渲染一共分为五步:

  1. 处理 HTML 并构建 DOM 树。
  2. 处理 CSS构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局,计算每个节点的位置。
  5. 调用GPU绘制,合成图层,显示在屏幕上。

具体过程如下图所示:

2.浏览器渲染原理详解

第一步:解析HTML标签,构建DOM树。

在这个阶段,引擎开始解析html,解析出来的结果会成为一棵dom树 dom的目的至少有2个: - 作为下个阶段渲染树状图的输入。 - 成为网页和脚本的交互界面。(最常用的就是getElementById等等)

当解析器到达script标签的时候,发生下面四件事情:

由此可以得到第一个结论:

第二步:解析CSS标签,构建CSSOM树

我们已经看到html解析器碰到脚本后会做的事情,接下来我们看下html解析器碰到样式表会发生的情况。js会阻塞解析,因为它会修改文档(document)。css不会修改文档的结构,如果这样的话,似乎看起来css样式不会阻塞浏览器html解析。但是事实上 css样式表是阻塞的。阻塞是指当cssom树建立好之后才会进行下一步的解析渲染。

通过以下手段可以减轻cssom带来的影响:

第三步:把DOM和CSSOM组合成渲染树(render tree)

注意: - link元素不会阻塞DOM Tree的构建过程,但是会阻塞Render Tree 的构建过程,这是因为 Render Tree 在构建时,需要对应的CSSOM Tree; - Render Tree 和 DOM Tree 并不是一一对应的关系,比如对于 display 为 none 的元素,压根不会出现在 render tree中;

第四步:在渲染树的基础上进行布局,计算每个节点的几何结构。

布局(layout):定位坐标和大小,是否换行,各种position, overflow, z-index属性。

第五步:调用 GPU 绘制,合成图层,显示在屏幕上。

将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting。

3.reflow与repaint

reflow:称为重排。是指布局或者几何属性发生改变就称为重排。

哪些操作导致重排呢?

repaint: 重绘。是指当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘。

哪些操作或者属性会引发重绘呢? - color - border-style - visibility - background - text-decoration - background-image - background-position - background-repeat - outline-color - outline - outline-style - border-radius - outline-width - box-shadow - background-size

结论:重排(reflow)必定会发生重绘(repaint),重绘(repaint)不一定会引发重排(reflow)。所以重排所需的成本比重绘高很多,改变深层次的节点很可能导致父节点的一系列重排。

如何减少重绘和重排?

1)全局范围重排。

看下面代码.

<body>
  <div class="hello">
    <h4>hello</h4>
    <p><strong>Name:</strong>BDing</p>
    <h5>male</h5>
    <ol>
      <li>coding</li>
      <li>loving</li>
    </ol>
  </div>
</body>

当p节点上发生reflow时,hello和body也会重新渲染,甚至h5和ol都会收到影响。 解决方案:用局部布局来解释这种现象:把一个dom的宽高之类的几何信息定死,然后在dom内部触发重排,就只会重新渲染该dom内部的元素,而不会影响到外界。

2)translate 替代 top

看下面代码。

<div class="test"></div>
<style>
    .test {
        position: absolute;
        top: 10px;
        width: 100px;
        height: 100px;
        background: red;
    }
</style>
<script>
    setTimeout(() => {
        // 引起重排
        document.querySelector('.test').style.top = '100px'
    }, 1000)
</script>

使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发重排(改变了布局)。

3)不要把 DOM 结点的属性值放在一个循环.

for(let i = 0; i < 1000; i++) {
    // 获取 offsetTop 会导致重排,因为需要去获取正确的值
    console.log(document.querySelector('.test').style.offsetTop)
}

还有其它的渲染的优化技巧比如: