在基本理解下面的内容后,再回过来看该图就会一目了然了
浏览器工作原理
当我们在浏览器中输入一个URL并发送请求时,主要会有如下几个处理步骤…
1. URL 解析与缓存检查
-
浏览器会先对 URL 进行解析,识别对应的协议(如 http、https)、域名、端口号和路径等信息
-
浏览器在发送网络请求前,会先检查一下本地缓存(如浏览器缓存、系统缓存等)中是否已有对应请求的资源的副本
浏览器缓存:
首先,浏览器会检查它的缓存中是否有这个域名的记录,因为之前访问过的网址的解析结果可能会被存储在浏览器缓存中
操作系统缓存:
如果浏览器缓存中没有找到,浏览器会询问操作系统,因为操作系统也可能有自己的DNS缓存
**ISP( Internet service provider)缓存:
如果以上都没有缓存记录,请求最终会发送到你的互联网服务提供商(ISP),它们通常会有更大范围的DNS缓存
-
如果本地缓存中存在对应的资源且在没有过期的情况下,直接从缓存中加载资源,来节省时间和带宽; 反之,如果本地缓存中不存在对应的资源或资源已过期时,就会发送对应资源的请求
- **
🔺在浏览器中可以通过一个比较简单的办法来查看请求的资源是否是通过缓存中进行获取:
**如果请求的资源是在本地缓存中加载的,通常对应请求的状态码为 "304 Not Modified",所以我们可以通过控制台中的 network 查看对应的状态码来进行查看
tip: 如果多次请求相同的资源没有使用缓存(状态码始终为 200)时,请先检查一下在 network 中是否禁用了缓存,如果打开了禁用缓存需要先取消掉禁用缓存项(如下图)
- **
2. DNS 域名解析:
如果在本地缓存中没有找到所需资源,浏览器会进行DNS查询,以获取域名对应的IP地址
DNS查询就变成了一个递归查询过程,涉及到多个DNS服务器
根域名服务器:
首先,你的DNS查询会被发送到根域名服务器(根服务器是最高级别的DNS服务器),负责重定向到管理顶级域名(如.com、.net等)的顶级域名服务器
顶级域名服务器(TLD服务器):
根服务器会告诉你的ISP的DNS服务器去查询哪个顶级域名服务器来找到 .com、.net.org 等域的信息 → 这个服务器掌握所有 .com 等域名及其相应服务器的信息
权威域名服务器(二级域名服务器):
一旦你的DNS查询到达了正确的顶级域名服务器,它会进一步定向到负责example.com的权威服务器 → 权威服务器有该域名对应的具体IP地址
tip → 权威域名服务器: 如我们可能比较熟知的阿里云、腾讯云、华为云等,这些大型云服务提供商都提供权威域名服务,帮助用户管理其域名的DNS记录...
3. 建立 TCP 连接
当获取到对应的 IP 地址后,通常会被存储在浏览器、操作系统、路由器或ISP的DNS缓存中,以便未来的查询可以更快得到解析 → 与服务器建立 TCP 连接
TCP (Transmission Control Protocol, 传输控制协议),TCP是一种面向连接的协议,用于在网络中的两个端点之间建立可靠的会话
以下是TCP连接建立过程,通常称为三次握手(TCP 3-way handshake):
SYN(Synchronize):
客户端发送一个SYN包到服务器以初始化一个连接
客户端发送一个SYN包到服务器以初始化一个连接
序列号不仅仅是在握手期间使用的,后续传输数据也会用到,用来保证数据的完整性和顺序
SYN-ACK(Synchronize-Acknowledgment):
SYN-ACK(Synchronize-Acknowledgment)
服务器同样设置一个随机的序列号,并将客户端的序列号加一,发送回给客户端,确认已经收到了客户端的同步请求(+1表示服务器确认收到)
ACK(Acknowledgment):
客户端收到服务器的SYN-ACK后,发送一个ACK包作为回应
这个ACK包将服务器的序列号加一,并可能包含客户端准备发送的数据的开始部分(比如HTTP请求行GET / HTTP/1.1和请求头,这个被称之为TCP快速打开)
此时,TCP连接已经建立,双方可以开始数据传输
4. 发送 HTTP 请求
-
当客户端与对应服务器已经建立了对应的 TCP 连接,浏览器就会构建 HTTP 请求报文等信息,并通过已建立的 TCP 连接向对应服务器发送相应的 HTTP 请求
客户端请求:
一旦TCP连接建立,客户端(通常是Web浏览器)就可以通过这个连接发送一个HTTP请求到服务器
这个请求包含了相应的请求报文、方法(GET、POST等)、URI(统一资源标识符)和协议版本,以及可能包含的请求头和请求体
服务器响应:
服务器接收请求后,解析请求报文,处理请求(如读取文件、查询数据库等),并构建HTTP响应报文返回给浏览器
响应报文包括状态行(HTTP版本、状态码、状态描述)、响应头和响应体 → 浏览器根据状态码和响应体内容处理请求结果
-
TCP为HTTP提供了一个可靠的通道,确保数据正确、完整地从服务器传输到客户端
5. 页面渲染
当浏览器接收到相应的 HTML 文档后,就会开始解析对应的 HTML 并构建对应的 DOM 树(Document Object Model)
同时,浏览器还会加载并解析对应的 CSS 样式表,构建对应的 CSSOM 树(CSS Object Model )
将 DOM 树与 CSSOM 树进行结合,生成对应的渲染树(Render Tree)
最终,浏览器会根据对应的渲染树(Render Tree)进行对应的布局与绘制操作,将页面内容渲染到屏幕中
tip: 更具体的解析过程如下面
6. 断开连接
当页面渲染完成且客户端和服务器之间的数据传输完成后,客户端与服务器就会断开对应的 TCP 连接(TCP 四次挥手)
四次挥手是一个标准的TCP连接断开过程,用于确保双方都能正常关闭连接并释放资源
浏览器页面解析渲染流程
在浏览器中一个网页下载下来后,通常由渲染引擎来进行解析渲染的
在解析过程中通常会包括多个阶段,如HTML解析、样式计算、构建渲染树、布局、绘制等步骤
-
1.HTML 解析(Parse HTML): 解析对应的 HTML 结构,生成对应的 DOM Tree
-
2.样式计算(Style Calculation): 浏览器在下载完 CSS 文件后,就会对 CSS 文件进行解析,解析出对应的规则树,可以称为 CSSOM(CSS Object Model : CSS对象模型)
-
渲染主线程会遍历DOM树,并为每个节点匹配对应的CSS规则,计算每个节点的最终样式
-
-
tip1: 在解析 html 中遇到 css 的 link 元素,浏览器就会下载并解析对应的 css 文件
-
tip2: 下载 css 文件是不会影响(阻塞)DOM 的解析的 → 即 CSS 可能会在 HTML 解析的同时或之后进行解析
-
-
3.构建渲染树(Render Tree): 当对应的 "DOM Tree" 与 "CSS Tree" 都解析完后,就会将该两个树进行结构构成出对应的 Render Tree 渲染树
-
-
tip1: link 元素虽然不会阻塞 DOMTree 的构建过程,但会阻塞 RenderTree 构建 → 因为在构建 RenderTree 时,是将 DOMTree 与 CSSTree 进行结合
-
tip2: RenderTree 和 DOMTree 并非一一对应的关系 → 如当 display 为 none 的元素,是不会在 RenderTree 上进行构建的
-
-
4.布局(Layout): 在渲染树(Render Tree)上进行布局(Layout)操作,计算每一个节点的几何信息(如宽高、位置...)等
布局完成后会得到一个相应的布局树(也称只为渲染树),它包含了所有节点需要渲染的节点及其样式的信息
tip1: 构建渲染树会表示显示哪些节点及其样式,但不会表示每个节点的尺寸、位置等信息 → 而布局是确定呈现树中所有节点的宽度、高度和位置信息
tip2: 此过程也称只为重排(Reflow)→ 因为当第一次布局后DOM树或CSSOM树发生变化时,需要重新计算布局
-
5.绘制(Paint): 将每个节点绘制(Paint)到屏幕上
在绘制阶段,浏览器将布局阶段计算的每个frame转为屏幕上实际的像素点
包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素(比如img)
-
第 4-5 布局与绘制图解
-
-
Match selectors:
浏览器遍历CSSOM,将选择器与DOM树中的元素匹配。这个过程决定了哪些CSS规则应用于哪些DOM元素
-
Compute style:
在选择器匹配后,浏览器计算每个元素的最终样式。这包括计算具体的样式值,处理继承的样式以及解析因层叠产生的任何冲突
-
Construct frames:
这通常是指生成布局树,它是渲染树的一部分,仅包含要布局和绘制的元素 → 这一步骤涉及确定文档的结构层次和包含块
tip: 布局树和渲染树是有微小的差异,布局树是渲染树的子集,不包含渲染树中元素的颜色、背景、阴影等信息
-
-
Reflow/Repaint 与 Composite 合成补充
认识回流与重绘(Reflow/Repaint)
-
Reflow:
-
在第一个次确定点的大小和位置,称之伟布局(layout),第一次布局之后再对节点的大小、位置修改需要重新进行计算的称之为回流(或重排)
-
通常在什么情况下会引起回流?
-
+ DOM 的结构发生改变 : 添加新的节点或者移除节点 + 布局发生了改变 : 修改了 width、height、padding、font-size 等会影响页面布局的值 + 窗口大小发生改变(resize) : 修改了浏览器窗口的尺寸等 + getComputedStyle : 调用 getComputedStyle 方法获取尺寸、位置信息
-
-
-
Repaint:
-
在第一次渲染内容时称绘制(paint),第一次绘制后重新进行渲染的称之为重绘
-
通常在什么情况下会引起重绘?
-
背景颜色、文字颜色、边框颜色、样式等
-
tip: 回流一定会引起重绘,所以回流时一件很消耗性能的事情
-
-
-
Reflow 和 Repaint 的性能优化:
-
1. 修改样式时尽量一次性的修改(如通过先定义将相应样式定义成相应的类型,通过修改对应 class 类名来统一修改多个样式等)
-
2. 避免频繁的 DOM 操作(如我们可以先在一个DocumentFragment或者父元素中,将要操作的所有的DOM操作先完成,再统一一次性的更改之真正的 DOM 中)
-
3. 避免通过 getComputedStyle 获取尺寸、位置等信息
→ 频繁调用getComputedStyle可能导致回流,因为浏览器需要提供准确的计算值
-
4. 对某些元素使用绝对定位或固定定位
→ 并不是不会引起回流,而是开销相对较小,不会对其他元素造成影响
-
额外方式(新的合成层( CompositingLayer )): 在特定情况下,可以创建新的合成层,并且新的图层可以利用GPU来加速绘制也可以提供性能
-
Composite 合成
-
再绘制过程中,可以将布局后的元素绘制到多个合成图层中(这是浏览器的一种优化手段)
-
默认情况下,标准流中的内容都是被绘制在同一个图层(Layer)中的
-
而一些特殊的属性,会创建一个新的合成层(CompositingLayer),并且在新的图层中可以利用 GPU 来加速绘制(因为每个合成层都是单独渲染的)
→ 通常有如下常见的属性或元素可以形成新的合成层 ↓
-
+ 3D transforms + video、canvas、iframe + opacity 动画转换时 + position: fixed + will-change:一个实验性的属性,提前告诉浏览器元素可能发生哪些变化 + animation 或 transition 设置了opacity、transform
-
-
tip:
分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用
tip:
该文章为个人的归纳总结的笔记,其中部分素材源自 coderwhy 老师