浏览器的渲染过程与原理
浏览器渲染页面的过程
- 解析URL
输入URL后,就会进行解析(URL的本质就是统一资源定位符)
URL一般包括几大部分:
protocol,协议头,譬如有http,ftp等
host ,主机域名或IP地址
port ,端口号
path ,目录路径
query ,即查询参数
fragment ,即#键后面的hash值,一般用来定位到某个位置
每次网络请求时都需要开辟单独的线程进行,如果URL解析到http协议,就会新建一个网络线程去处理资源下载
- DNS 解析(域名解析为id地址)
1.浏览器会先检查自身缓存中有没有被解析过的这个域名对应的IP,如果有,解析结束;
2.如果浏览器缓存没有命中,浏览器会检查操作系统缓存中是否有对应的解析过的结果,操作系统也有一个域名解析的过程,在hosts文件里可以读写,如果在这里找到了对应的映射关系,则会直接使用这个IP地址,完成域名解析;
3.如果还是没有命中,此时会请求本地域名服务器(LDNS)来解析这个域名,这个服务器一般离你不会很远且性能很强,80%的域名解析到这里就会结束;
4.如果LDNS仍然没有命中,此时会直接到Root Server域名服务器请求解析
非法网站或未备案网站,在域名服务器查询时会被拦截。
- TCP 连接
三次握手过程
建立tcp连接就是三次握手,三次握手的过程采用 TCP 协议,其可以保证信息传输的可靠性,三次握手过程中,若一方收不到确认信号,协议会要求重新发送信号
- 第一次:客户端发送SYN包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;seq=n是客户端的序列号。
- 第二次:服务器收到SYN包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
ack=x+1,返回客户端序列号加1,代表确认收到信息,seq=y代表服务端序列号。- 第三次:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
ack = y+1确认收到服务端序列号
为什么三次不是两次
在网络环境比较复杂的情况,客户端可能会连续发送多次请求。如果只设计成两次握手的情况,服务端只能一直接收请求,然后返回请求信息,也不知道客户端是否请求成功。这些过期请求的话就会造成网络连接的混乱。
不过为了节省资源,三次握手就可以符合实际情况,所以就没必要设计成四次握手、五次握手等等情况
- HTTP 请求即响应
经过TCP的三次握手之后,浏览器会对请求进行包装,包装成请求报文。完整的HTTP请求包含请求起始行、请求头部、请求主体三部分。
- 服务器响应
服务器在接收到浏览器发送的HTTP请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包含状态码,响应头,响应报文三个部分。
- 客户端渲染
本文讨论第五个部分,即浏览器对内容的渲染,这一部分(渲染树构建、布局及绘制),又可以分为下面五个步骤:
- 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象。
- 构建CSS规则树:生成CSS规则树(CSS Rule Tree)
- 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
- 布局(Layout):计算出每个节点在屏幕中的位置
- 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。
浏览器渲染详细过程:
- 浏览器已经拿到了 server 端返回的 HTML 内容,开始解析并渲染。最初拿到的内容就是一堆字符串,必须先结构化成计算机擅长处理的基本数据结构,这个内部结构就是 DOM,DOM 提供了对 HTML 文档的结构化表述。渲染进程通过分词器将html字节流成功成一个个 token,包括 Tag token 和文本 token。HTML 解析器维护了一个 token 栈结构,token 会按照对应顺序入栈出栈,然后将 token 解析成 DOM 节点,并将 DOM 节点添加进 DOM 树中。
- 解析过程中,如果遇到< link href ="…">和< script src ="…">这种外链加载 CSS 和 JS 的标签,浏览器会异步下载,下载过程和上文中下载 HTML 的流程一样。只不过,这里下载下来的字符串是 CSS 或者 JS 格式的。
- 渲染引擎在接受到 CSS 文本时,会将 CSS 生成 CSS对象模型CSSOM(即CSS Object Model) ,通过document.styleSheets可获取所有CSS样式表,然后将 styleSheet 中的属性值进行标准化操作。最后将 DOM 和 CSSOM 整合成 RenderTree ,然后针对 RenderTree 进行渲染。
- 最后,渲染过程中,如果遇到< script >就停止渲染,执行 JS 代码。因为浏览器渲染和 JS 执行共用一个线程,而且这里必须是单线程操作,多线程会产生渲染 DOM 冲突。待< script >内容执行完之后,浏览器继续渲染。
需要明白,这五个步骤并不一定一次性顺序完成。如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。实际页面中,CSS 与 JavaScript 往往会多次修改 DOM 和 CSSOM,下面就来看看它们的影响方式。
- 关闭TCP连接
关闭TCP连接通道
通过四次挥手释放TCP连接; 前 两 次挥手用于关闭一个方向的数据通道,后两次挥手用于关闭另外一个方向的数据通道。
四次挥手过程
- 客户端向服务器发送TCP报文,客户端进⼊ FIN_WAIT_1 状态。
TCP 头部中有:标记位FIN(finish),表示请求释放连接;Seq=U 客户端序列号
客户端进入半关闭阶段,并且停止向服务端发送通信数据。- 服务端收到FIN,回复TCP报文,接着服务端进⼊ CLOSED_WAIT 状态。
ACK(标记位Ack=U+1)
TCP 头部中有:标记位ACK;确认号 U + 1
客户端收到 TCP 报文后,确认服务器已经收到了客户端连接释放的请求,随后客户端结束 FIN-WAIT-1 阶段,进入 FIN-WAIT-2 阶段- 服务器端会将遗留的待传数据传送给客户端,之后再次向客户端发送 TCP 报文,服务端进⼊ LAST_ACK 状态,并且停止向客户端发送数据。
TCP 头部中有:标记位FIN;Seq=Y 服务端序列号- 客户端收到FIN,TCP报文,之后进⼊ TIME_WAIT 状态。
TCP 头部中有:标记位ACK;Seq=Y+1 服务端序列号
服务端收到了 ACK 应答报⽂后,就进⼊了 CLOSED 状态,⾄此服务端已经完成连接的关闭。
客户端在经过 2MSL ⼀段时间后,⾃动进⼊ CLOSED 状态,⾄此客户端也完成连接的关闭。因为第四次挥手不一定能成功发给服务端,所以要等一下,看看服务端会不会因为没收到第四次挥手,而重发第三次挥手。
四次挥手原因
- 我们一步一步来分析,首先第一次挥手一定是不能省略的,否则服务端不知道客户端要断开连接。
- 再来看第二次挥手和第三次挥手,关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。同时此时服务端也很有可能还要处理一些数据,
而服务端的数据处理不完是不可能直接给客户端相应可以关闭连接的报文的,这会导致数据丢失。因此,服务端必须额外发送一次报文,用于通知客户端自己的数据已经处理完毕。
那么能不能在服务端数据处理完之后直接回复给客户端FIN+ACK呢?也是不行的。因为如果要处理的数据太多,客户端会长时间得不到回应。这可能会导致客户端认为自己的数据包丢失。然后客户端会重发FIN报文,导致资源浪费。
因此FIN和ACK是必要分两次与客户端进行挥手的- 那么能不能省去最后一步挥手呢?也是不行的。如果服务端在响应ACK后直接关闭的话,如果最后一次挥手的数据包丢失,那么将导致客户端无法关闭。