浏览器工作原理
查看更多学习笔记:GitHub:LoveEmiliaForever
MDN中文官网
导航
导航是加载 web 页面的第一步:输入 URL、点击一个链接、提交表单等等
DNS查询
导航的第一步是要去寻找页面资源的位置
例如访问https://example.com
,如果以前没有访问过它,则要向名称服务器
发起DNS查询
DNS查询的目的是期望得到网址所对应服务器的IP地址
第一次请求之后,这个 IP 地址可能会被缓存一段时间
通过主机名加载一个页面通常仅需要一次 DNS 查询
但是,如果你的页面里面有多个不同的网址,例如网络字体
、网络图像
、广告
等的请求地址分别是不同的网址,那么就要进行多次DNS查询
DNS 查询可能存在性能问题,特别是对于移动网络
移动网络中每一个 DNS 查询必须从手机发送到基站,然后到达一个认证的 DNS 服务器
这是一个比较大的等待时间
TCP握手
一旦获取到服务器 IP 地址,浏览器就会通过 TCP三次握手
与服务器建立连接
通过 TCP 首先发送了三个消息进行协商,然后在两台电脑之间开始一个 TCP会话
这意味着终端与每台服务器之间还要来回发送三条消息,而请求尚未发出
TLS协商
对于通过 HTTPS
建立的安全连接,还需要另一次握手,即TLS协商
它会决定使用哪种密码对通信进行加密,验证服务器,并在开始实际数据传输前建立安全连接
这一过程需要在实际发送内容请求之前进行,再往返服务器五次
响应
建立了和服务器的连接后,浏览器将自动发送一个HTTP GET 请求
,以请求初始文件
首字节时间TTFB
是用户通过点击链接进行请求与收到第一个 HTML 数据包之间的时间
第一个内容分块通常是 14KB 的数据
拥塞控制/TCP慢启动
TCP 数据包在传输过程中被分成若干段,由于 TCP 保证数据包的顺序,因此服务器在发送一定数量的数据包后,必须以 ACK 数据包的形式收到客户端的确认
- 如果服务器在每个网段后都等待 ACK,则会导致客户端频繁发出 ACK
- 如果客户端无法接收到网段,不停地回应 ACK,服务器将一直重新发送网段
为了找到最合适的网段包含数据量,TCP 慢启动算法
会逐渐增加传输数据量,直到确定最大网络带宽,往后它还将根据网络负载进行动态调整
传输段的数量由拥塞窗口CWND
的值控制,该值可初始化为 1、2、4 或 10 MSS(以太网中MSS为1500 bytes),客户端收到后必须发送 ACK,如ACK正常,则CWND翻倍,如异常,CWND减半
解析
解析
是浏览器将通过网络接收的数据转换为 DOM 和 CSSOM 的步骤
在浏览器收到数据的第一块时,它就可以开始解析收到的信息
即使请求页面的 HTML 大于初始的 14KB 数据包,浏览器也将开始解析并尝试根据其拥有的数据进行渲染,但最好在前 14KB 中包含浏览器开始渲染页面所需的所有内容
这 14KB 至少应包含页面模板(第一次渲染所需的 CSS 和 HTML)
构建DOM树
DOM 树描述了文档的内容,因此处理 HTML 标记并构造 DOM 树是第一步进行的,如果文档格式良好,则解析它会简单而快速,DOM 节点的数量越多,构建 DOM 树所需的时间就越长
当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析
当遇到一个 CSS 文件时,解析也可以继续进行
但是对于<script>
标签(特别是没有 async 或 defer)会阻塞渲染并停止 HTML 的解析
当 JavaScript 解析和执行顺序不重要时,可以添加 async 属性或 defer 属性,以减少阻塞
等待获取 CSS 不会阻塞 HTML,但是它会阻塞 JavaScript,因为JS通常会查改CSS
预加载扫描器
在构建DOM树时,构建程序会占用主线程,在此时,预加载扫描仪将解析现阶段可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体
如此,我们不必等到解析器找到对外部资源的引用来请求它,而是异步进行
构建CSSOM树
CSSOM和DOM相似,都是树,不过它们两是独立的数据结构
浏览器将 CSS 规则转换为可以理解和使用的样式映射,它会遍历CSS规则,并创建节点树
浏览器会从节点的最通用规则开始,通过应用更具体的规则递归地优化计算的样式
构建 CSSOM 非常快,它甚至小于一次DNS查询的时间
JS编译
JS 会被解析、编译、解释
- 脚本被解析为抽象语法树
- 将抽象语法树输入编译器,输出字节码即进行编译
- 解释时,大部分代码在主线程上进行解释,但如多线程worker的代码例外
渲染
渲染步骤包括样式、布局、绘制,在某些情况下还包括合成
CSSOM 树和 DOM 树组合成渲染树,用于计算每个可见元素的布局,将其绘制到屏幕上
某些情况下,可以将内容提升到它们自己的层并进行合成,通过在 GPU 而不是 CPU 上绘制屏幕的一部分来提高性能,从而释放主线程
样式
计算样式树或渲染树的构建从 DOM 树的根开始,遍历每个可见节点
应用了 visibility: hidden
的节点会包含在渲染树中,因为它们会占用空间
而不可见和display:none
的节点都不会被包含在渲染树中
每个可见节点都应用了 CSSOM 规则,渲染树包含所有可见节点的内容和计算样式
,将所有相关样式与 DOM 树中的每个可见节点匹配起来,并根据 CSS 级联,确定每个节点的计算样式
布局
在渲染树上运行布局以计算每个节点的几何体
确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置
渲染树会标识显示哪些节点及其计算样式,但不标识每个节点的尺寸或位置
为了确定每个对象的确切大小和位置,浏览器会从根开始遍历渲染树
由于设备关系,会有很多不同的视区大小(如1080p和720p的屏幕就不一样)
考虑到视口大小,浏览器将确定屏幕上所有不同框的尺寸
以视口的大小为基础,布局通常从 body 开始,用每个元素的框模型属性排列所有 body 的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间
第一次确定节点的大小和位置称为布局,随后对节点大小和位置的重新计算称为回流
假设布局发生在返回图像之前,一旦知道图像的尺寸,就会出现回流
绘制
在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素
绘画包括将元素的每个可视部分绘制到屏幕上,浏览器会非常快速的完成此项工作
为保障质量,浏览器通常以60Hz的速度绘制
绘制可以将布局中的元素分解为多个层,有一些特定的属性和元素可以实例化一个层(<video>
、<canvas>
、css属性有opacity
、3d transform
、will-change
的元素)
分层可以提高性能,但它是以内存管理为代价的,因此不应过度使用
合成
当文档的各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容
回流会触发重新绘制和重新合成
交互
可交互时间TTI
是测量从第一个请求导致 DNS 查询和 SSL 连接到页面可交互时所用的时间
可交互是 First Contentful Pain1 之后的时间点,页面在 50ms 内响应用户的交互
如果主线程正在解析、编译和执行 JavaScript,则JS不可用,因此无法及时(小于 50ms)响应用户交互
首次内容绘制,浏览器首次渲染任何可见元素 ↩︎