一分钟快速了解从URL输入到页面渲染的全流程

lxf2023-11-18 16:30:01

前言

渲染引擎是现代浏览器中最核心的部分之一,它负责将 HTML、CSS 和 JavaScript 等网页资源解析并展示给用户。虽然大多数用户并不关心渲染引擎的工作原理,但了解它的工作原理可以帮助我们前端工程师更好地理解网页的呈现方式,以及如何优化网页性能和用户体验。 在本文中,我们将深入探讨渲染引擎的工作原理、渲染流程、重绘和回流机制等核心概念。我们将从用户输入网址开始,再到HTML 文档的解析和构建 DOM 树,介绍 CSS 样式的解析和计算、渲染树的生成和绘制,以及 JavaScript 对网页渲染的影响等方面。通过这些内容的讲解,我们将揭示浏览器中最复杂和关键的部分之一,帮助我们更深入地了解现代浏览器的工作原理和实现机制。

导航

导航是加载 web 页面的第一步。它发生在以下情形:用户通过在地址栏输入一个 URL、点击一个链接、提交表单或者是其他的行为。 Web 性能优化的目标之一就是缩短导航完成所花费的时间,在理想情况下,它通常不会花费太多的时间,但是等待时间和带宽会导致它的延时。

DNS 查询

浏览器首先需要将用户输入的网址转换成相应的 IP 地址,这个过程称为 DNS 解析。浏览器会向本地 DNS 服务器发送一个 DNS 查询请求,本地 DNS 服务器会将这个请求转发到根域名服务器、顶级域名服务器、权威域名服务器等,最终查询到目标网站的 IP 地址,并返回给浏览器。 DNS 查询可能存在性能问题,特别是对于移动网络。当一个用户使用了移动网络,每一个 DNS 查询必须从手机发送到基站,然后到达一个认证的 DNS 服务器。手机、信号塔、名称服务器之间的距离可能是一个大的时间等待。

一分钟快速了解从URL输入到页面渲染的全流程

TCP 握手

在得到服务器 IP 地址后,会通过 TCP 三次握手与服务器建立连接,以便将请求发送给目标网站。浏览器会向目标网站的服务器发送一个 SYN 报文段,如果服务器端回复一个 SYN/ACK 报文段,浏览器就会发送一个 ACK 报文段,这样就建立了一个 TCP 连接。

TLS 协商

HTTPS 的链接还需要再进行 TLS 协商以确保数据传输的安全性。浏览器会向服务器发送一个 Client Hello 报文,其中包含支持的 TLS 版本、加密算法等信息。服务器会回复一个 Server Hello 报文,其中包含选择的 TLS 版本、加密算法等信息。接着,服务器会向浏览器发送一个证书,浏览器会验证证书的有效性,并生成一个随机数,用于生成会话密钥。浏览器会将这个随机数加密后发送给服务器,服务器解密后也生成一个会话密钥。之后,双方就可以使用会话密钥加密和解密数据了。

一分钟快速了解从URL输入到页面渲染的全流程 在以上工作完成后,我们的浏览器才可以发起 HTTP 请求。

响应

我们和服务器建立连接之后,浏览器会自动发送一个 GET 请求,对于网站来说,这个请求返回的经常是一个 HTML 文件。因为 TCP 的慢启动规则,初次请求的内容大小通常是 14KB 的数据,在收到初始包数据之后,服务器会将下一个包的数据大小翻倍,也就是说,后续的数据包依次是前一个包大小的二倍,直到达到预定的阈值或遇到拥塞。

解析

当浏览器收到数据后,就可以正式开始解析了,整个流程可以用以下这张图大致表示,接下来我们一步一步看。

一分钟快速了解从URL输入到页面渲染的全流程

第一步 构建 DOM 树

浏览器是不能直接理解 HTML 结构的,需要将 HTML 标签用 HTML 解析器转换成浏览器可理解的树形 DOM 结构。当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。当遇到一个 CSS 文件时,解析也可以继续进行,但是对于 <script> 标签(特别是没有 async 或者 defer 属性的)会阻塞渲染并停止 HTML 的解析。尽管浏览器的预加载扫描器加速了这个过程,但过多的脚本仍然是一个重要的瓶颈。

第二步 构建 CSSOM 树

同样的,浏览器也不能直接理解 CSS 代码,所以也需要用 CSS 解析器构建 CSSOM 树。不同的是,DOM 树表示的是 HTML 标签的层级关系, 而 CSSOM 树则表示选择器之间的层级关系,而且 CSSOM 的构建速度非常快。实际上,浏览器在构建 DOM 树的过程中,如果发现有 CSS 资源,则会使用预加载扫描器去下载,下载完成后会同步构建 CSSOM 树,CSSOM 树主要有两个目的:

  • 给 JavaScript 提供操作样式的能力
  • 为渲染树的合成提供基础的样式信息

其他过程: JavaScript 编译

在解析 HTML 时,如果发现有 JavaScript 资源,也会用预加载扫描器去下载该 JavaScript 文件,正如上面所说,如果你的 <script> 标签没有加 asyncdefer 属性,会立即阻塞 HTML 的解析,去下载并执行 JavaScript 代码。 注意:如果 JavaScript 代码中有操作 CSS 属性的部分,则浏览器会停止解析 JavaScript,先加载 CSS,所以 CSS 有可能会阻塞 JavaScript。 deferasync 属性都是去异步加载外部的JS脚本文件,它们则不会阻塞 DOM 的解析,其区别如下:

  • 执行顺序:
    • 多个带 async 属性的标签,不能保证加载的顺序;
    • 多个带 defer 属性的标签,按照加载顺序执行;
  • 脚本是否并行执行:
    • async 表示脚本的下载是异步的,而下载完毕后如果 DOM 未解析完,则阻塞 DOM 解析,先执行脚本。
    • defer 表示脚本的下载是异步的,而执行 JavaScript 脚本需要等到文档所有元素解析完成之后,DOMContentLoaded 事件触发执行之前才执行。

第三步 组合 Render 树

将 DOM 和 CSSOM 组合成一个 Render 树,计算样式树或渲染树从 DOM 树的根开始构建,遍历每个可见节点。像 <head> 和它的子节点以及任何具有 display: none 样式的结点不会出现在 Render 树上。而具有 visibility: hidden 的节点会出现在 Render 树上,因为它们会占用空间。

第四步 Layout 布局

第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。假设我们的页面中 <img> 标签的资源下载比较慢,初始布局发生在返回图像之前。由于我们没有声明图像的大小,因此一旦知道图像大小,就会有回流。

第五步 Painting 绘制

最后一步是将各个节点绘制到屏幕上,绘制时可以将布局树中的元素分解为多个层。将内容提升到 GPU 上的层(而不是 CPU 上的主线程)可以提高绘制和重新绘制性能。有一些特定的属性和元素可以实例化一个层,包括 <video><canvas>,任何 CSS 属性为 opacity3D transformwill-change 的元素等等。 之后会将每个图层生成绘制列表,再交由合成线程处理,合成线程再将图层分成不同的图块,并通过栅格化将图块转换成位图,最后把绘制图块的指令发送给浏览器进程,浏览器把图块绘制到屏幕上显示给用户。 值得注意的是,浏览器针对页面的回流与重绘,进行了自身的优化——渲染队列, 浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。

交互

一旦主线程绘制页面完成,你可能会认为我们已经“准备好了”,但事实并非如此。如果加载包含 JavaScript(并且延迟到 onload 事件激发后执行),则主线程可能很忙,无法用于滚动、触摸和其他交互。

总结

至此,从输入地址开始,到浏览器渲染引擎将页面呈现给用户的过程就分析完成了。总结大致流程如下:

  • 输入地址,按下回车;
  • DNS 查询服务器 IP;
  • TCP 三次握手;
  • TLS 协商;
  • 发送初始 GET 请求到 HTML 文件;
  • 将 HTML 内容构建成 DOM 树;
  • 将 CSS 内容构建成 CSSOM 树;
  • 将 DOM 树和 CSSOM 树合成渲染树;
  • 根据渲染树进行页面元素的布局;
  • 对渲染树进行分层操作,并生成分层树;
  • 为每个图层生成绘制列表,并提交到合成线程;
  • 合成线程将图层分成不同的图块,并通过栅格化将图块转化为位图;
  • 合成线程给浏览器进程发送绘制图块指令;
  • 浏览器进程会生成页面,并显示在屏幕上。

我是荼锦,一个兴趣使然的开发者。非常感谢您阅读本文,希望本文对您了解从URL输入到页面渲染的全流程有所帮助。

尽管我在本文中尽可能正确地描述渲染引擎的工作原理,但是难免会存在一些错误和不足之处。如果您在本文中发现了任何错误或者有歧义之处,非常欢迎您指正并提出建议,以便我能够不断改进和提升文章的质量。

再次感谢您的阅读,希望本文对您有所帮助!

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!