怎么保证页面文件被送达到浏览器

483 阅读5分钟

浏览器渲染页面的几个阶段。

构建 DOM 树=>样式计算=>布局阶段=>分层=>绘制=>分块=>光栅化和合成。

构建DOM树

DOM是保存在内存中的数状结构,可以通过JavaScript来查询或修改其内容。

样式计算

渲染引擎会把获取到的 CSS 文本全部转换为 styleSheets 结构中的数据,并且该结构同时具备了查询和修改功能,这会为后面的样式操作提供基础。

样式的来源途径

    1 通过 link 引用的外部 CSS 文件
    2 <style>标记内的 CSS
    3 元素的 style 属性内嵌的 CSS
  • 1浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。 如输入 document.styleSheets

  • 2转换样式表中的属性值,使其标准化

    如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。

  • 3计算出 DOM 树中每个节点的具体样式

    CSS 的继承规则和层叠规则

    层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位. 继承指在css中所有自元素都会继承父元素的样式。

       body { font-size: 20px }
       p {color:blue;}
       span  {display: none}
       div {font-weight: bold;color:red}
       div  p {color:green;}
    

布局阶段

构建布局
1 遍历 DOM 树中的所有可见节点,并把这些节点加到布局中;
2 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

分层

因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树。

什么样的元素会被提升为单独的一层?

定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等,都拥有层叠上下文属性。

页面是个二维平面,但是层叠上下文能够让 HTML 元素具有三维概念,这些 HTML 元素按照自身属性的优先级分布在垂直于这个二维平面的 z 轴上。

使用overflow裁剪也会产生分层。

图层绘制

在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制。在进行绘制的过程中,其实就是在执行一系列的绘制指令。

实际上绘制操作是由渲染引擎中的合成线程来完成的。

当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程 而实际上 合成线程会将图层划分为图块,合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。

栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

合成和显示

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。

总结

从 HTML 到 DOM、样式计算、布局、图层、绘制、光栅化、合成和显示。

渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。

渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。

  • 创建布局树,并计算元素的布局信息。

  • 对布局树进行分层,并生成分层树。

  • 为每个图层生成绘制列表,并将其提交到合成线程。

  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。

  • 合成线程发送绘制图块命令DrawQuad给浏览器进程。

  • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

重排

改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

重绘

如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

减少重排和重绘的操作。

相当于少了渲染进程的主线程和非主线程的很多计算和操作,能够加快web的展示。

1 重排 重绘的操作尽量放在一起,比如 改变浏览器dom的高度和设置margin分开写,可能会触发两次重排。
2 使用 class 操作样式,而不是频繁操作 style
3 避免使用 table 布局
4 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
4 对 resize 等事件防抖处理
5 dom 属性读写分离
6 使用 will-change: transform 做优化