渲染页面:浏览器的工作原理

大部分情况下,浏览器是单线程执行的.为了有流畅的交互,需要尽可能快的发送请求,减少网络等待时间.Web性能优化则需要尽可能快的

页面资源的访问

DNS 查找

  • 缓存
  • 递归
  • 权威
  • DNS 查找的问题
    • 移动端距离
    • TCP 协议
    • TLS 加密传输协议

TCP

8次往返后浏览器才发出请求

服务端响应

与 web 服务器建立连接后, 浏览器才发送初始的 HTTP GET 请求获取页面资源

响应

  • 一旦服务器收到请求,使用相关的 响应头 和 HTML 回复
  • 初始请求的响应包含所接收数据的第一个字节
  • “Time to First Byte” (TTFB) 是进行请求到收到第一个包之间的时间
  • 第一块 内容通常是14kb

TCP 慢开始 / 14kb 规则

  • 第一个响应包是 14kb 大小
  • 慢开始是一种均衡网络连接的速度的算法,逐渐增减发送数据的大小,直到最大网络带宽
  • 在收到 ACK 后 会加倍 包的大小 (直到阈值 或 遇到拥塞)

TCP slow start

这就是为什么web性能优化需要将此初始14Kb响应作为优化重点的原因

  • 比如 webpack 打包文件的大小限制

    拥塞控制

拥塞控制是作用于网络,它是防止过多的包被发送到网络中,避免出现网络负载过大,网络拥塞的情况。

  • 避免发送包速率过快导致,客户端丢弃数据包
  • 包丢弃意味着,将不会有确认帧的返回。服务器把它们当做确认帧丢失。拥塞控制算法使用这个发送包和确认帧流来确定发送速率
  • 拥塞控制算法 - 拥塞状态机 - 各状态下的发包策略及状态间的变更条件

TCP重点系列之拥塞状态机

延伸: 移动端的拥塞控制算法Verus

解析

一旦浏览器收到数据的第一块,就可以开始解析收到的信息.即使请求页面的HTML大于初始的14kb数据包. 推测性解析,解析 是浏览器将数据转为DOM 和 CSSOM的步骤,解析后通过渲染器把 DOM+CSSOM在屏幕上绘制.

这就是为什么前14kb 最好包含浏览器开始渲染页面所需要的所有内容,或至少包含页面模版(第一次渲染所需的CSS和HTML).但在渲染到屏幕上面之前,HTML,CSS,JavaScript必须被解析完成

构建DOM树

第一步:处理HTML标记并构造DOM树.HTML解析涉及到DOMTokenization 和 DOM 树的构造.DOM 格式良好 会解析的简单而快速.DOM节点数量越多,构建DOM树所需的时间越长

DOM树构造

  • 当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析.当遇到CSS文件时,也可以继续进行
  • 阻塞资源 <script> 标签 (特别是没有async 或者 defer 属性)则会阻塞渲染并停止HTML的解析.
  • 所以过多脚本是一个重要的性能瓶颈

预加载扫描器

  • 浏览器构建DOM树,占用了主线程.
  • 预加载扫描器将解析可用的内容并请求高优先级资源,如 CSS,JavaScropt 和 web Fonts.
  • 不必等到解析起找到对外部资源的引用来请求他.
  • 减少了阻塞,优化渲染速度

当主线程在解析HTML和CSS时,预加载扫描器将找到脚本和图像,并开始下载它们。为了确保脚本不会阻塞进程,当JavaScript解析和执行顺序不重要时,可以添加async属性或defer属性。

等待获取CSS不会阻塞HTML的解析或者下载,但是它的确阻塞JavaScript,因为JavaScript经常用于查询元素的CSS属性。

构建 CSSOM 树

第二步: 处理CSS并构建CSSOM树.

  • CSS对象模型和DOM是相似的.
  • CSSOM 和 DOM 是两棵树,有独立的数据结构
  • 浏览器将CSS规则转换为可以理解和使用的样式映射.
  • 浏览器便利CSS中的规则集,根据CSS选择器创建具有父/子/兄弟关系的节点树
  • 浏览器构建CSSOM 相比于DOM ,规则更具体,级联属性强,非常快 ,小于一次DNS查找所需的时间

其他过程

  • JavaScript 编译
    • 当CSS被解析并创建CSSOM时,其他资源正在下载(preload scanner 预加载扫描器).随后JavaScript被解析,编译,执行.
    • 脚本被解析为抽象语法树.
    • 浏览器引擎使用抽象语法树,并将其传递到解释器中,输出在主线程上执行的字节码,这就是所谓的JavaScript编译
  • 构建辅助功能树
    • 用于可访问对象模型 (AOM),类似于 DOM的语义版本
    • 当 DOM更新时,浏览器会更新辅助功能树
    • 辅助功能树无法修改可访问性树
    • 构建AOM之前,屏幕阅读器(scree readers) 无法访问内容

渲染

渲染包括 布局,样式,绘制,在某些情况下还包含合成.

  • 在解析步骤中创建的CSSOM树 和DOM树 组合成一个Render树
  • 用于计算每个可见元素的布局,然后将其绘制到屏幕上
  • 在某些情况下,可以将内容提升到它们自己的层,并进行合成,通过GPU来绘制屏幕的一部分提高性能,释放主线程

Style

第三步: 将DOM树 和 CSSOM树组合成Render树,计算样式树或渲染树丛 DOM树的根开始构建,遍历每个可见节点

  • <head> 标签和它的字节点
  • 以及任何具有 { display: none;} 样式的节点
  • 这些标签都不会显示,也就是不会出现在Render树上.
  • 但 具有 visibility: hidden 的节点会出现在Render树上,因此它们会占用空间
  • <script> 节点也不会包含在Render树中

Render树保存所有具有内容和计算样式的可见节点——将所有相关样式匹配到DOM树中的每个可见节点,并根据CSS级联确定每个节点的计算样式。

Layout

第四步: 在Render树上执行布局处理逻辑,计算出每个节点的几何体.布局处理逻辑是确定呈现树中所有节点的宽度,高度和位置,以及确定页面上每个对象的大小和位置的过程

  • 构建渲染树后,开始布局
  • 渲染树标识显示哪些节点(即使不可见)及其计算样式.但不标识每个节点的尺寸或位置
  • 为了确定每个对象的确切大小和位置,浏览区从渲染树的根开始遍历它
  • 首先确定尺寸,不同节点 类似不同盒子
  • 按照视区大小为基础,布局从body开始,用每个元素的框模型属性排列所有body的子孙元素的尺寸,为无法确定尺寸的元素(例如未设置大小的img标签)提供占位符空间
  • 第一次确定节点的大小和位置称之为布局.
  • 随后对节点大小和位置的重新计算称为回流.(img标签)

Paint

最后一步: 将各个节点绘制到屏幕上,第一次出现的节点称为
first_meaningful_paint.
在绘制或光栅格化阶段,浏览器将在布局阶段计算的每一个框转换为屏幕上的实际像素(所以浏览器可以有绘制过程的展示设置-Show paint rectangles)

  • 绘制包括将元素的每个可视部分会知道屏幕上,包括
    颜色,文本,边框,阴影和提花呢元素(按钮/图像)
  • 为了确保平滑滚动和动画,占据主线程的所有内容,包括样式计算,以及回流和绘制,必须让浏览器在16.67毫米内完成.
  • 为了确保重绘的速度比初始绘制速度更快,屏幕上的绘图通常被分解成数层.如果发生这种情况,则需要进行合成.
  • 绘制可以将DOM树上的元素分解为多个层.将内容提升到GPU上的层(而不是CPU上的主线程),可以提高绘制和重绘的性能.
  • 有一些特定的属性和元素可以实例化一个层,包括<video><canvas>,任何CSS属性为 opacity, 3D转换,will-change的元素,还有一些其他元素.
  • 这些节点将于子节点一起绘制到他们自己的层上,除非子节点由于上述一个或者多个原因需要自己的层
  • 层绘制 确实可以提高性能,但是它以内存管理为代价,因此不作为web性能优化策略而过度使用的一部分

Compositing

  • 当DOM树各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容.

  • 当页面继续加载资源时,可能会发生回流(类似图片加载),回流会出发重新绘制和重新组合.如果我们定义了图像的大小,只需重新绘制需要更新的层,并在必要的时候进行合成.
    但没有设置大小则会在获取图像后,渲染过程将返回到布局步骤并从那里开始重新开始(回流)

交互

  • 主线程绘制完页面后,并没有完全准备好

  • 如果加载了 JavaScript ,并且延迟到onload 事件触发后执行,则主线程可能很忙,无法用于滚动,触摸和其他交互

  • TTI (Time to Interactive) 是测量第一个请求从DNS查询和SSL链接到页面可交互时所花费的时间

  • 可交互是 “First Contentful Paint”之后的时间点. 页面可在50ms内响应用户的交互

  • 如果主线程正在解析,编译和执行JavaScript,则它不可用,因此无法及时(小于50ms) 响应用户请求

现象: 用户可以非常快地看到页面,但是在下载、解析和执行脚本之前,就无法滚动。主线程在这段时间内完全被占用,对单击事件或屏幕点击没有响应。

实验观察

  • 本地页面文件,浏览器打开
  • 控制台设置加载资源的速度
  • 验证页面资源加载对页面绘制的影响
  • CSS 加载对 HTML/JS 的影响
  • 打开 Rendering -> Paint flashing 绘制布局
  • 打开 Show overview / Capture screenshots 绘制过程

引用