Skip to content

Latest commit

 

History

History
119 lines (97 loc) · 12.5 KB

02-渲染页面:浏览器的工作原理.md

File metadata and controls

119 lines (97 loc) · 12.5 KB

web 用户体验两重点: 页面内容快速加载流畅的交互影响 Web 性能的两大主因: 等待资源加载时间大部分情况下的浏览器单线程执行

网络等待时间是在链路上传送二进制到电脑端所消耗的链路传输时间。Web 性能优化需要做的就是尽可能快的使页面加载完成

大部分情况下,浏览器是单线程执行的。为了有流畅的交互,开发者的目标是确保网站从流畅的页面滚动到点击响应的交互性能
渲染时间是关键要素,确保主线程可以完成所有给它的任务并且仍然一直可以处理用户的交互。
通过了解浏览器单线程的本质与最小化主线程的责任可以优化 Web 性能,来确保渲染的流畅和交互响应的及时。

页面渲染过程

【导航】: 用户通过在地址栏输入一个 URL、点击一个链接、提交表单或者是其他的行为。

  • 在理想情况下,它通常不会花费太多的时间,但是*等待时间(**[Web 性能优化]*的目标之一)和带宽会导致它的延时

【DNS 查询】: 浏览器向名称服务器发起 DNS 查询请求,最终得到一个 IP 地址。对于页面指向的不同的主机名,则需要多次 DNS 查询。

  • 第一次请求之后,这个 IP 地址可能会被缓存一段时间
    • 这样可以通过从缓存里面检索 IP 地址而不是再通过名称服务器进行查询来加速后续的请求。
  • DNS 查询可能存在性能问题,特别是对于移动网络
    • 当一个用户使用了移动网络,每一个 DNS 查询必须从手机发送到基站,然后到达一个认证的 DNS 服务器。
    • 手机、信号塔、名称服务器之间的距离可能是一个大的时间等待

【TCP 握手】: 一旦获取到服务器 IP 地址,浏览器就会通过 TCP“三次握手” 与服务器建立连接。这个机制的是用来让两端尝试进行通信。

  • 在浏览器和服务器通过上层协议 HTTPS 发送数据之前,可以协商网络 TCP 套接字连接的一些参数。

【TLS 协商】: 为了在 HTTPS 上建立安全连接,TLS 协商是必须的

  • 它决定了什么密码将会被用来加密通信,验证服务器,在进行真实的数据传输之前建立安全连接。
    • 在发送真正的请求内容之前还需要三次往返服务器。
  • 虽然建立安全连接对增加了加载页面的等待时间,对于建立一个安全的连接来说,以增加等待时间为代价是值得的
    • 因为在浏览器和 web 服务器之间传输的数据不可以被第三方解密

【响应】: 一旦我们建立了到 web 服务器的连接,浏览器就代表用户发送一个初始的 HTTP GET 请求,对于网站来说,这个请求通常是一个 HTML 文件。一旦服务器收到请求,它将使用相关的响应头和 HTML 的内容进行回复

  • TCP 慢启动 / 14KB 规则: 第一个响应数据包是 14KB 大小的。这是慢启动的一部分,慢启动是一种均衡网络连接速度的算法。
    • 慢启动逐渐增加发送数据的数量直到达到网络的最大带宽。
    • 在 TCP 慢启动 中,在收到初始包之后,服务器会将下一个数据包的大小加倍到大约 28KB。
      • 后续的数据包依次是前一个包大小的二倍直到达到预定的阈值,或者遇到拥塞
    • TCP 慢启动就是初始响应为 14KB 的原因,也是为什么 **[web 性能优化]**需要将此初始 14KB 响应作为优化重点的原因。
      • 如果能够将渲染所需要的资源控制在 14kb 之内,那么就能 TCP 协议启动时,一次完成数据的传递。
    • TCP 慢启动逐渐建立适合网络能力的传输速度,以避免拥塞。
  • 拥塞控制: 当服务器用 TCP 数据包来发送数据时,客户端通过返回确认帧来确认传输。
    • 由于硬件和网络条件,连接的容量是有限的。如果服务器太快地发送太多的包,它们可能会被丢弃。
    • 这意味着,将不会有确认帧的返回。服务器把它们当做确认帧丢失。
    • 拥塞控制算法使用这个发送包和确认帧流来确定发送速率

【解析】: 一旦浏览器收到数据的第一块,它就可以开始解析收到的信息。

  • 解析是浏览器将通过网络接收的数据转换为 DOM 和 CSSOM 的步骤,通过渲染器把 DOM 和 CSSOM 在屏幕上绘制成页面。
    • DOM 是浏览器标记的内部表示。DOM 也是被暴露的,可以通过 JavaScript 中的各种 API 进行 DOM 操作。
  • 即使请求页面的 HTML 大于初始的 14KB 数据包,浏览器也将开始解析并尝试根据其拥有的数据进行渲染。
    • 这就是为什么*在前 14KB 中包含浏览器开始渲染页面所需的所有内容,或者至少包含页面模板(第一次渲染所需的 CSS 和 HTML)对于 [web 性能优化]来说是重要的。但是在渲染到屏幕上面之前,HTML、CSS、JavaScript 必须被解析完成*。
  • 解析的一些操作(带“第 X 步”的是在 关键渲染路径(CRP) 的五个步骤中,渲染部分一致):
    • 构建 DOM 树第一步是处理 HTML 标记并构造 DOM 树。DOM 树描述了文档的内容。
      • 当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。
      • 当遇到一个 CSS 文件时,解析也可以继续进行,
      • 但是对于 <script> 标签(特别是没有 async 或者 defer 属性的)会阻塞渲染并停止 HTML 的解析
        • 尽管浏览器的预加载扫描器加速了这个过程,但过多的脚本仍然是一个重要的瓶颈
    • 预加载扫描器: 浏览器构建 DOM 树时,这个过程占用了主线程。此时预加载扫描仪将解析可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体。
      • 预加载扫描器在后台检索资源,以便在主 HTML 解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。
      • 预加载扫描仪提供的优化减少了阻塞。
      • 等待获取 CSS 不会阻塞 HTML 的解析或者下载,但是它确实会阻塞 js,因为 js 经常用于查询元素的 CSS 属性。
    • 构建 CSSOM 树: 第二步是处理 CSS 并构建 CSSOM 树。DOM 和 CSSOM 是相似的两棵树,是独立的数据结构。浏览器将 CSS 规则转换为可以理解和使用的样式映射。浏览器遍历 CSS 中的每个规则集,根据 CSS 选择器创建具有父、子和兄弟关系的节点树。
      • chrome 开发工具 performance 录制一段->network->下方 eventLog,找 Recalculate Style,显示解析 CSS、构建 CSSOM 树和递归计算计算样式所需的总时间。在 **[web 性能优化]**方面,它是可轻易实现的,因为创建 CSSOM 的总时间通常小于一次 DNS 查询所需的时间。
    • JavaScript 编译: JavaScript 被解释、编译、解析和执行。脚本被解析为抽象语法树。一些浏览器引擎使用抽象语法树并将其传递到解释器中,输出在主线程上执行的字节码。这就是所谓的 JavaScript 编译。
      • 当 CSS 被解析并创建 CSSOM 时,其他资源,包括 JavaScript 文件正在下载(借助预加载扫描器)。
    • 构建辅助功能树: 浏览器还构建辅助设备用于分析和解释内容的辅助功能(accessibility)树。
      • 无障碍对象模型(AOM)类似于 DOM 的语义版本。当 DOM 更新时,浏览器会更新辅助功能树。辅助技术本身无法修改无障碍树。

【渲染】: 渲染步骤包括样式(Style)、布局(Layout)、绘制(Paint),在某些情况下还包括合成(Compositing)。

  • 在解析步骤中创建的 CSSOM 树和 DOM 树组合成一个 Render 树,然后用于计算每个可见元素的布局,然后将其绘制到屏幕上。
  • 样式: 第三步是将 DOM 和 CSSOM 组合成一个 Render 树,计算样式树或渲染树从 DOM 树的根开始构建,遍历每个可见节点。
    • <head> 和它的子节点以及任何具有 display: none 样式的结点不会出现在 Render 树上。
    • 具有 visibility: hidden 的节点出现在 Render 树上,因为它们会占用空间。
    • 每个可见节点都应用了其 CSSOM 规则。Render 树保存所有具有内容和计算样式的可见节点
      • 将所有相关样式匹配到 DOM 树中的每个可见节点,并根据 CSS 级联确定每个节点的计算样式。
  • 布局: 第四步是在渲染树上运行布局以计算每个节点的几何体。
    • 布局与回流
      • **布局(Layout)**是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。
      • **回流(Reflow)**是对页面的任何部分或整个文档的任何后续大小和位置的确定。
      • 第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。
    • 构建渲染树后,开始布局。渲染树标识显示哪些节点(即使不可见)及其计算样式,但不标识每个节点的尺寸或位置。
      • 为了确定每个对象的确切大小和位置,浏览器从渲染树的根开始遍历它。
  • 绘制: 最后一步是将各个节点绘制到屏幕上。
    • 第一次出现的节点称为 first meaningful paint。在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素。
    • 绘制包括将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。浏览器需要快速完成。
      • 为了确保重绘的速度比初始绘制的速度更快,屏幕上的绘图通常被分解成数层。如果发生这种情况,则需要进行合成
      • 绘制可以将布局树中的元素分解为多个层。将内容提升到 GPU 上的层(而不是 CPU 上的主线程)可以提高绘制和重新绘制性能。
      • 分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用
  • 合成: 当文档的各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容。
    • 当页面继续加载资产时,可能会发生回流,回流会触发重新绘制和重新组合。
      • 示例: 解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。
        • 如果定义了图像的大小,就不需要重新绘制,只需要重新绘制需要重新绘制的层,并在必要时进行合成。
        • 如果没有包括图像大小,从服务器获取图像后,渲染过程将返回到布局步骤并从那里重新开始。

【交互】: 主线程绘制页面完成,并不一定可以交互。(等待 JS 引擎解析过程完成)

  • 如果加载包含 js(并且延迟到 onload 事件激发后执行),则主线程可能很忙,无法用于滚动、触摸和其他交互。
  • TTI(Time to Interactive)是测量从第一个请求导致 DNS 查询和 SSL 连接到页面可交互时所用的时间。
    • 可交互是 First Contentful Paint 之后的时间点,页面在 50ms 内响应用户的交互。
  • 如果主线程正在解析、编译和执行 JavaScript,则它不可用,因此无法及时(小于 50ms)响应用户交互。

关键渲染路径(Critical rendering path)

  • 理解 CRP: 是浏览器将 HTML,CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列。
    1. 在解析 HTML 时会创建文档对象模型。
    • HTML 可以请求 JavaScript,而 JavaScript 反过来,又可以更改 DOM。
    1. HTML 包含或请求样式,依次来构建 CSS 对象模型。
    2. 浏览器引擎将两者结合起来以创建渲染树。
    3. 布局确定页面上所有内容的大小和位置。
    4. 确定布局后,将像素绘制到屏幕上。
  • 优化 CRP: 提升页面加载速度需要通过被加载资源的优先级、控制它们加载的顺序和减小这些资源的体积。
    1. 通过异步重要资源的下载来减小请求数量,
    2. 优化必须的请求数量和每个请求的文件体积,
    3. 通过区分关键资源的优先级来优化被加载关键资源的顺序,来缩短关键路径长度。