在浏览器地址栏输入正确的 url 到整个页面渲染完成,参考网上给出的回复大致分为以下步骤:
- 浏览器自动补全协议、端口
- 浏览器自动完成url编码
- 浏览器根据url地址查找本地缓存,根据缓存规则看是否命中缓存,若命中缓存则直接使用缓存,不再发出请求
- 通过DNS解析找到服务器的IP地址
- 浏览器向服务器发出建立TCP连接的申请,完成三次握手后,连接通道建立
- 若使用了HTTPS协议,则还会进行SSL握手,建立加密信道。使用SSL握手时,会确定是否使用HTTP2
- 浏览器决定要附带哪些cookie到请求头中
- 浏览器自动设置好请求头、协议版本、cookie,发出GET请求
- 服务器处理请求,进入后端处理流程。完成处理后,服务器响应一个HTTP报文给浏览器。
- 浏览器根据使用的协议版本,以及Connection字段的约定,决定是否要保留TCP连接。
- 浏览器根据响应状态码决定如何处理这一次响应
- 浏览器根据响应头中的Content-Type字段识别响应类型,如果是text/html,则对响应体的内容进行HTML解析,否则做其他处理
- 浏览器根据响应头的其他内容完成缓存、cookie的设置
- 浏览器开始从上到下解析HTML,若遇到外部资源链接,则进一步请求资源
- 解析过程中生成DOM树、CSSOM树,然后一边生成,一边把二者合并为渲染树(rendering tree),随后对渲染树中的每个节点计算位置和大小(reflow),最后把每个节点利用GPU绘制到屏幕(repaint)
- 在解析过程中还会触发一系列的事件,当DOM树完成后会触发DOMContentLoaded事件,当所有资源加载完毕后会触发load事件
上述步骤简单理解可以分为三步:补全输入的网址,DNS解析拿到IP地址;遵循网络通信的协议将请求发送到目标IP,目标IP返回所需资源;浏览器拿到响应资源按照一定规则渲染页面。按照如上三步补足和完善自己的知识点。
网址解析
URL组成
浏览器地址栏可以输入任意的字符串,当输入的是一个网址时,页面会跳转到对应的页面,当输入的是一个非网址的字符串时,将会作为一个关键字进行搜索。输入的网址可以是一个域名,也可以是一个完整的 URL 。
URL(Uniform Resource Locator) 统一资源定位器,是互联网上标准资源的地址。URL 和 URN (Uniform Resource Name) 是 URI (Uniform Resource Identifier) 的子集。
完整的 URL 会有以下6部分组成:
- 协议:protocol,可以是 http 和 https,https 会将数据进行加密处理相较于 http 更加安全。
- 域名:domain,通过域名可以得到ip地址
- 端口号:port,指定服务器连接的网络端口号。此项也是可选项,若用户省略则自动使用默认端口号。http默认是80,https 默认是 443。
- 路径:path,指定服务器上的文件路径来定位特指的资源。
- 查询参数:search,针对已指定的文件路径内的资源,可以使用查询字符串传入任意参数。此项可选。
- 锚点:hash,使用片段标识符通常可标记出已获取资源中的子资源(文档内的某个位置)。该项也为可选项。
通过 URL 能够知道资源的具体地址,但因为互联网中所有主机是通过ip进行统一寻址。浏览器会发送一个UDP的包给DNS域名解析服务器,通过 DNS 域名解析获取主机的 ip 地址。
域名解析
域名是由其所属各级域名及其自身名字共同组成(即由子域名构成),级别从左到右依增加,最右边为顶级域名,最左边为主机自己的名字,各级子域名使用“.”隔开,常见的格式如下:
主机名.机构名.网络名.顶级域名
参与域名解析过程最重要的单元就是域名服务器,域名服务器的结构如下:
- 根域名服务器,是全球级别最高,最重要的域名服务器,全世界共有13台(IPv4根域名服务器,编号为A到M),1个主根服务器和9个辅根服务器在美国,欧洲2个辅根服务器,位于英国和瑞典,亚洲1个辅根服务器,位于日本。根域名服务器只纪录其下级顶级域名服务器的域名及其IP地址,当低级域名服务器遇到无法解析的域名时,首先会向根域名服务器求助。
- 顶级域名服务器,级别同顶级域,用于纪录注册在该顶级域名服务器上的所有二级域名并提供DNS查询服务。
- 权限域名服务器,为一个区域的主机提供DNS查询服务,如果查询结果为空,则通知发起请求的DNS用户应到哪个权限域名服务器进一步查询。
- 本地域名服务器,在域名解析中扮演重要的角色(未在上图中体现)。每主机发出的DNS域名查询请求首先都会发送到本地域名服务器。本地域名服务器可以设立在个人,大学,公司等各种范围内,又叫做首选DNS(很熟悉吧),就是我们计算机网络连接中的首选DNS。
借助下面的一张图看懂域名解析全过程:
上图以用户访问谷歌网站为例,简明扼要的为大家讲述了用户输入网址到获取IP地址的全过程,下面是详细的过程:
- 用户打开计算机,在浏览器中输入谷歌网址后计算机将向本地DNS服务器发起域名解析请求。本地DNS服务器通常由互联网服务提供商(ISP)提供,如三大运营商。
- 本地DNS服务器接收到用的DNS请求后,首先查询其自身缓存纪录中是否存在谷歌域名对应的IP地址,如果存在,则直接将该IP地址回传给用户计算机;否则,将进一步向根域名服务器发起求助。
- 由于根域名服务器只会纪录其下级的13个顶级域名服务器,而不会直接纪录域名与IP的映射关系,所以在接收到本地域名服务器的解析请求时,根域名服务器将告知本地服务器:“你所请求的域名由.com顶级域名服务器管理,其IP为xxx”。
- 本地DNS服务器进一步向.com顶级域名服务器发起域名解析请求,由于.com域名服务器也不会纪录域名与IP的映射关系,而是告知请求者去该域名所属的域服务器上查询,并给出其IP地址。
- 本地DNS服务器继续向域服务器发起头条域名解析请求,便会得到头条域名对应的IP地址,这时本地DNS服务器不仅会向用户计算机返回IP地址,同时在其自身缓存中增加头条域名与其IP的纪录,从而加快其他计算机获取头条域名对应IP的解析速度。
网络通信
有了主机ip之后,接下来就是将请求发送给目标主机,目标主机根据请求的内容返回所需的资源。在这过程中,请求信息和返回的资源在不同的软硬件(如客户端的应用软件,网卡,路由器,交换机,主机中的服务程序)中进行通信。不同的厂商生产不同的硬件或者软件其内部构造不尽相同,保证不同硬件或者软件间的通信就需要使用相同的协议,此外,通过分层可以细分通信功能,更易于单独实现每个分层的协议,界定各个分层的具体责任和义务。
网络模型和协议
ISO(国际标准化组织)制定了国际标准OSI七层网络模型(开放式通信系统互联参考模型),IETF(国际互联网工程任务组)制定了 TCP/IP 四层网络模型,将TCP/IP接口层替换为 OSI 七层模型中的数据链路层和物理层的TCP/IP 五层网络模型,下图为网络模型的说明:
OSI七层网络模型及其协议图
TCP/TP协议
TCP/IP (Transmission Control Protocol/Internet Protocol)为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议等组成(当然还有其他后来发展起来的网络协议,还包括 ARP,ICMP,IGMP,UDP,以及让域名访问成为可能的DNS,以及电脑/手机可以自动获取IP地址的DHCP。当然还有形形色色的应用层的协议如 HTTP / SMTP / FTP 等)。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。
IP协议
IP协议(Internet Protocol)定义了如何在网络中传输数据包,以及如何在网络中定位计算机,是将多个包交换网络连接起来。它将数据报文分割成小的数据包,然后在网络中传输,最后在接收端重新组装成完整的数据报文。IP协议具有如下特点:
- IP协议是一种无连接、不可靠的分组传送服务的协议。
- IP协议是点-点线路的网络层通信协议。IP协议是针对原主机-路由器、路由器-路由器、路由器-目的主机之间的数据传输的点-点线路的网络层通信协议。
- IP协议屏蔽了网络在数据链路层、物理层协议与实现技术上的差异。
目前,主流IP是基于IPv4的。IPv4报文结构用于提供IP数据报的源和目的地址,由9个字段组成,结构如下图所示:
-
版本号:用于指示IP数据报使用的IP协议版本
-
首部长度:这个4位字段定义了数据报首部的长度,以4字节的字为单位。当首部没有选项时,首部结构20字节;当这个字段值位最大值F时,首部长度最大为60字节。
-
服务类型:在最初这个字段有一部分用于定义数据报的优先级,剩下的一部分定义了服务类型。IETF已经改变了这个8位字段的解释,现在定义了一组区分服务。在这种解释中,前6位构成了码点(codepoint),最后两位未使用。当码点字段最右边的3位不全为0时,这6位定义了54种服务(低延时,高吞吐量等等)。
-
总长度:这个16位字段定义了数据报总长度,其以字节为单位。故IPv4数据报总长度上限值位65536字节。注:为什么需要这个字段?在许多情况下,我们确实不需要这个字段值。但是有些情况下,封装在一个帧里的并不仅仅是数据报,还可能附加了一些填充。比如,以太网协议对帧的数据有最大值(1500字节)和最小值(46字节)的限制,当数据小于46字节时,数据将含有填充数据。
-
标识(identification):这个16位字段标志了从源主机发出的一个数据报,这样就确定了数据报的唯一性。这样使得数据报被分片后,在到达终点时终点能根据标识号将同一个数据报的分片重新组装成一个数据报。
-
标志(flag):第一位保留(未用),第二位为“不分片(do not fragment)”,第三位“还有分片(more fragment)”。
-
分片偏移:这个13位字段表示的是分片在整个数据报中的相对位置。这是数据在原始数据报中的偏移量,以8字节位单位。
-
生存时间:这个8位字段用来控制数据报所经过的最大跳数(路由器),每经过一个路由器,这个字段数值都减1,减1后变位0时,路由器就丢弃这个数据报。
-
协议:这个8位字段定义了使用IPv4服务的高层协议,如TCP,UDP,ICMP,IGMP,OSPF等的数据都将被封装到IP数据报中。这个字段指明数据报必须交付给哪个最终目的协议。
-
检验和:检验IP数据报首部。
-
源地址:定义了源点的IP地址,这个字段4字节始终保持不变。
-
目的地址:定义了终点的IP地址,这个字段4字节始终保持不变。
IPV4地址:规定IP地址长度为32(按TCP/IP参考模型划分) ,即有2^32-1个地址,用点分隔的四段数字表示,每段可以是0~255之间的数字。IPv4地址由如下两部分组成:
- 网络号码字段(Net-id):用来标识一个网络。
- 主机号码字段(Host-id):用来区分一个网络内的不同主机。对于网络号相同的设备,无论实际所处的物理位置如何,它们都是处在同一个网络中。
为了方便IP地址的管理及组网,IP地址分成五类,如下图:
IP地址分类及范围
网络类型 | 地址范围 | 说明 |
---|---|---|
A | 0.0.0.0~127.255.255.255 | 全0的主机号码表示该IP地址就是网络的地址,用于网络路由;全1的主机号码表示广播地址,即对该网络上所有的主机进行广播。 |
B | 128.0.0.0~191.255.255.255 | 全0的主机号码表示该IP地址就是网络的地址,用于网络路由;全1的主机号码表示广播地址,即对该网络上所有的主机进行广播。 |
C | 192.0.0.0~223.255.255.255 | 全0的主机号码表示该IP地址就是网络的地址,用于网络路由;全1的主机号码表示广播地址,即对该网络上所有的主机进行广播。 |
D | 224.0.0.0~239.255.255.255 | D类地址是一种组播地址。 |
E | 240.0.0.0~255.255.255.255 | 保留。255.255.255.255用于局域网广播地址。 |
子网划分,通过将IP地址的网络部分进一步划分为若干个子网,解决了IP地址空间利用率低和两级IP地址不够灵活的问题。掩码:是用来指定网络中的子网掩码,它也是由32位二进制数字组成的,用点分隔的四段数字表示,每段可以是0~255之间的数字。将IP地址与其相应掩码位执行与运算一起用来确定网络中的计算机属于哪个子网,从而实现网络通信。
为了解决IPv4网络地址资源有限的问题提出IPV6协议。IPv6是一种新的Internet协议,它比IPv4更先进,拥有更大的地址空间,支持更多的设备连接,并且提供更高的安全性和性能。
IPv6报文格式由以下几部分组成:
- Version:版本号,长度为4bit。对于IPv6,该值为6。
- Traffic Class:流类别,长度为8bit。等同于IPv4中的TOS字段,表示IPv6数据报的类或优先级,主要应用于QoS。
- Flow Label:流标签,长度为20bit。IPv6中的新增字段,用于区分实时流量,不同的流标签+源地址可以唯一确定一条数据流,中间网络设备可以根据这些信息更加高效率的区分数据流。
- Payload Length:有效载荷长度,长度为16bit。有效载荷是指紧跟IPv6报头的数据报的其它部分(即扩展报头和上层协议数据单元)。该字段只能表示最大长度为65535字节的有效载荷。如果有效载荷的长度超过这个值,该字段会置0,而有效载荷的长度用逐跳选项扩展报头中的超大有效载荷选项来表示。
- Next Header:下一个报头,长度为8bit。该字段定义紧跟在IPv6报头后面的第一个扩展报头(如果存在)的类型,或者上层协议数据单元中的协议类型。
- Hop Limit:跳数限制,长度为8bit。该字段类似于IPv4中的Time to Live字段,它定义了IP数据报所能经过的最大跳数。每经过一个设备,该数值减去1,当该字段的值为0时,数据报将被丢弃。
- Source Address:源地址,长度为128bit。表示发送方的地址。
- Destination Address:目的地址,长度为128bit。表示接收方的地址。
IPv6地址格式是一个128位的二进制地址,它由8个16位的十六进制数字组成,每个数字之间用冒号分隔,例如:2001:0db8:85a3:0000:0000:8a2e:0370:7334。
TCP协议
TCP协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。用于两台计算机之间的数据传输。它使用三次握手确认连接,并且在数据传输过程中提供可靠的数据传输服务。TCP协议还提供了流量控制、拥塞控制和差错控制等功能,以确保数据的可靠传输。TCP协议的特点有:
- 面向连接,建立连接后才能传输数据;
- 可靠性高,采用三次握手机制,保证数据的可靠传输;
- 流量控制,发送端不能发送太多数据,接收端也不能接收太多数据;
- 拥塞控制,当网络拥塞时,发送端会减少发送数据的速度;
- 确认和重传,发送端会确认接收端是否收到数据,如果没有收到,发送端会重新发送数据。
TCP报文是TCP协议发送的数据包,它由报文首部和数据组成,报文首部包含源端口、目的端口、序号、确认号等信息,用于控制数据传输。TCP报文格式如下:
如上图所示,TCP数据格式是由若干具有特殊含义字段组成的。其中,
- 源端口(Source Port)和目标端口(Destination Port):分别占用16位,表示源端口号和目的端口号;用于区别主机中的不同进程, 而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接;
- 序号(Sequence Number):用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据流中的序号;主要用来解决网络报乱序的问题;
- 确认号(Acknowledgment Number):32位确认序列号包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志(下面介绍)为1时该确认序列号的字段才有效。主要用来解决不丢包的问题;
- 数据偏移(Offset):给出首部中32 bit字的数目,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能 表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段, 正常的长度是20字节;
- 标志位(TCP Flags):TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次 为URG,ACK,PSH,RST,SYN,FIN。
URG:此标志表示TCP包的紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促 中间层设备要尽快处理这些数据;
ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1, 为1的时候表示应答域有效,反之为0;
PSH:这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序, 而不是在缓冲区中排队;
RST:这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包;
SYN:表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1, ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送 一个只有SYN的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这 种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全 的主机将会强制要求一个连接严格的进行TCP的三次握手; - 窗口(Window):窗口大小,也就是有名的滑动窗口,用来进行流量控制。
- 检验和:检验TCP数据报首部。
TCP协议的三次握手和四次挥手
三次握手具体过程(状态)如下:
- 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认。(客户的建立连接并等待确认)
- 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态。(服务器端发送相关报文段信息并等待连接)
- 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。(客户的接收到服务端信息并实现连接)
四次挥手具体过程(状态)如下:
- 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了。(一方数据发送完成)
- 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我也没有数据要发送了,可以进行关闭连接了。(另一方数据发送完成)
- 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入CLOSE_WAIT状态。(请求关闭连接并等待)
- 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。(关闭连接)
HTTP协议
HTTP(HyperText Transfer Protocol)协议是一种用于从万维网服务器传输超文本到本地浏览器的传输协议。它是一个客户端-服务器协议,通常由一个Web浏览器作为客户端,向运行在服务器端的HTTP服务器发出请求。HTTP协议基于TCP/IP通信协议,是一种无状态协议,每次连接的初始状态都是一样的。HTTP有以下特点:
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
- 支持B/S及C/S模式。
HTTP报文由请求报文和响应报文组成,其报文结构如下:
HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。对于客户端用户来说,这些信息中的大部分内容都无须亲自查看。
在请求中,HTTP 报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。在响应中,HTTP 报文由 HTTP 版本、状态码(数字和原因短语)、HTTP 首部字段 3 部分构成。
HTTP 首部字段是由首部字段名和字段值构成的,中间用冒号“:” 分隔,格式如下:
首部字段名: 字段值
HTTP 首部字段的设置可以控制浏览器的一些行为,如缓存和状态管理等。
浏览器缓存策略是指浏览器在访问网页时,会将网页的内容缓存到本地,以便下次访问时可以直接从本地读取,从而提高网页的加载速度。浏览器缓存的策略主要有强缓存和协商缓存两种。
强缓存是指浏览器会根据网页的响应头中的Cache-Control和Expires字段来判断是否缓存,如果符合条件,则会将网页缓存到本地。再次请求后如果缓存未过期它不会发送请求到服务器,而是直接从客户端缓存中获取资源;如果缓存过期则使用协商缓存。
Cache-Control 字段值(常用) | 字段值说明 |
---|---|
no-cache(默认值) | 告知(代理)服务器不直接使用缓存,要求向原服务器发起请求(协商缓存) |
no-store | 所有内容都不会被保存到缓存或Internet临时文件中 |
max-age=seconds | 缓存内容将在seconds秒后失效 |
public | 所有内容都将被缓存(客户端和代理服务器都可缓存) |
private | 所有内容只有客户端可以缓存 |
协商缓存是指浏览器会根据网页的响应头中的Last-Modified和Etag字段来判断是否缓存。
当强缓存失效后判断是否存在Etag,存在则会带上if-none-match:[保存的etag的值]
向服务器发起请求,通过发送的Etag的值和服务端的Etag的值进行比对,如果一致代表资源没有改变,服务端返回正文为空的响应,状态码304,告诉浏览器从缓存中读取资源。
判断是否存在 last-modified,存在则带上if-modified-since:[保存的last-modified的值]
。根据浏览器发送的修改时间和服务端的修改时间进行比对,一致的话代表资源没有改变,服务端返回正文为空的响应,状态码304,让浏览器中缓存中读取资源。
【拓展知识】缓存的存储位置
缓存的存储位置有4个,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。这四种依次为:
-
Service Worker:
Service Worker是运行在浏览器背后的独立线程,可用于实现离线缓存、消息推送、后台同步等功能。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。
-
Memory Cache:内存中,主要包含的是当前页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据比磁盘快,但是一旦我们关闭页面,内存中的缓存将会被释放。
-
Disk Cache:硬盘中,读取速度慢点,但是什么都能存储到磁盘中,比 Memory Cache 胜在容量和存储时效性上。
-
Push Cache:推送缓存是属于 HTTP/2 中新增的内容。
当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在 Chrome 浏览器中只有 5 分钟左右,同时它也并非严格执行 HTTP/2 头中的缓存指令。
关于Push Cache更多的信息资可以阅读 Jake Archibald 的 HTTP/2 push is tougher than I thought 这篇文章。
文章中的几个结论:
- 所有的资源都能被推送,并且能够被缓存,但是 Edge 和 Safari 浏览器支持相对比较差
- 可以推送 no-cache 和 no-store 的资源
- 一旦连接被关闭,Push Cache 就被释放
- 多个页面可以使用同一个 HTTP/2 的连接,也就可以使用同一个 Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的 tab 标签使用同一个 HTTP 连接。
- Push Cache 中的缓存只能被使用一次
- 浏览器可以拒绝接受已经存在的资源推送
- 可以给其他域名推送资源
HTTP状态管理指在HTTP协议中,服务器和客户端之间通过HTTP头部信息来维护状态的一种技术。它可以让服务器跟踪用户的状态,从而实现更好的用户体验。HTTP状态管理的主要技术有Cookie和Session。
Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。服务器端发送的响应报文内存在 Set-Cookie 的首部字段信息会通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
Set-Cookie 字段值已键值对的形式表示,常见的值如下:
属性 | 说明 |
---|---|
NAME=VALUE | 赋予 Cookie 的名称和其值(必需项) |
domain=域名 | 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie 的服务器的域名) |
Secure | 仅在 HTTPS 安全通信时才会发送 Cookie |
HttpOnly | 加以限制,使 Cookie 不能被 JavaScript 脚本访问 |
maxAge | cookie 失效的时间,单位秒。如果为整数,则该 cookie 在 maxAge 秒后失效。如果为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie 。如果为 0,表示删除该 cookie 。默认为 -1。(比 expires 好用) |
expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
session 是另一种记录服务器和客户端会话状态的机制,其基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中。
session 认证流程:
- 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session(一个对象),将此 Session 的唯一标识信息 SessionID 返回给浏览器
- 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
- 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
【扩展知识】浏览器离线存储
数据处理流程
数据在各层网络模型传递,每个分层中,都会对所发送的数据附加一个首部,在这个首部中包含了该层必要的信息,如发送的目标地址以及协议相关信息。通常,为协议提供的信息为包首部,所要发送的内容为数据。在下一层的角度看,从上一层收到的包全部都被认为是本层的数据。
数据处理流程,下图以用户 a 向用户 b 发送邮件为例子:
整个的数据处理流程说明如下:
-
应用程序处理:首先应用程序会进行编码处理,这些编码相当于 OSI 的表示层功能;编码转化后,邮件不一定马上被发送出去,这种何时建立通信连接何时发送数据的管理功能,相当于 OSI 的会话层功能。
-
TCP 模块的处理:TCP 根据应用的指示,负责建立连接、发送数据以及断开连接。TCP 提供将应用层发来的数据顺利发送至对端的可靠传输。为了实现这一功能,需要在应用层数据的前端附加一个 TCP 首部。
-
IP 模块的处理:IP 将 TCP 传过来的 TCP 首部和 TCP 数据合起来当做自己的数据,并在 TCP 首部的前端加上自己的 IP 首部。IP 包生成后,参考路由控制表决定接受此 IP 包的路由或主机。
-
网络接口(以太网驱动)的处理:从 IP 传过来的 IP 包对于以太网来说就是数据。给这些数据附加上以太网首部并进行发送处理,生成的以太网数据包将通过物理层传输给接收端。
-
网络接口(以太网驱动)的处理:主机收到以太网包后,首先从以太网包首部找到 MAC 地址判断是否为发送给自己的包,若不是则丢弃数据。如果是发送给自己的包,则从以太网包首部中的类型确定数据类型,再传给相应的模块,如 IP、ARP 等。这里的例子则是 IP 。
-
IP 模块的处理:IP 模块接收到 数据后也做类似的处理。从包首部中判断此 IP 地址是否与自己的 IP 地址匹配,如果匹配则根据首部的协议类型将数据发送给对应的模块,如 TCP、UDP。这里的例子则是 TCP。另外吗,对于有路由器的情况,接收端地址往往不是自己的地址,此时,需要借助路由控制表,在调查应该送往的主机或路由器之后再进行转发数据。
-
TCP 模块的处理:在 TCP 模块中,首先会计算一下校验和,判断数据是否被破坏。然后检查是否在按照序号接收数据。检查端口号,确定具体的应用程序。数据被完整地接收以后,会传给由端口号识别的应用程序。
-
应用程序的处理:接收端应用程序会直接接收发送端发送的数据。通过解析数据,展示相应的内容。
页面渲染
浏览器的网络线程会发送 http 请求,和服务器之间进行通信,浏览器将拿到的 html 页面封装成一个渲染任务,并将其传递给渲染主线程的消息队列。在事件循环机制的作用下,渲染主线程取出消息队列中的渲染任务,开启渲染流程。整个渲染流程分为多个阶段,分别是: HTML解析、样式计算、布局、分层、生成绘制指令、分块、光栅化、绘制
HTML 解析
当浏览器接服务器发送的数据后(此时数据为字节数据,即都是由01组成),会先将这些字节数据转换为字符串。数据转换为字符串以后,浏览器会先将这些字符串通过词法分析转换为标记( token ),这一过程在词法分析中叫做标记化( tokenization )。在此基础上分析出DOM节点,并构建DOM节点数。
解析 HTML 的过程会占用了主线程。为了加快需要从外部引入资源(如CSS、JavaScript 和 web 字体)的效率,浏览器在开始解析前,会启动一个预解析的线程,率先下载 HTML 中的外部 CSS 文件和 JS 文件。外部的 CSS 文件如果还没有下载解析好,主线程不会等待,继续解析后续的 HTML,不会阻塞进程。主线程解析到 script 位置,会停止解析 HTML,转而等待 JS 文件下载好,并将全局代码解析执行完成后,才能继续解析 HTML,导致线程阻塞。另外,在现代浏览器中,通过在 script 标签上添加 async / defer 为我们提供了新的方式来避免 JS 代码阻塞渲染的情况
<script async src="./async.js"></script>
<script defer src="./defer.js"></script>
最后总结一下此阶段的成果,HTML 解析完成后,会得到 DOM 树和 CSSOM 树,浏览器的默认样式、内部样式、外部样式、行内样式均会包含在 CSSOM 树中:
样式计算
拥有了 DOM 树我们还不足以知道页面的外貌,因为我们通常会为页面的元素设置一些样式。主线程会遍历得到的 DOM 树,依次为树中的每个节点计算出它最终的样式,称之为 Computed Style。在这一过程中,很多预设值会变成绝对值,比如 red 会变成 rgb(255,0,0);相对单位会变成绝对单位,比如 em 会变成 px。所有的没有设置的样式属性会被赋予默认值。详细的计算过程可以参考 [译]CSS 属性计算过程。
css 样式计算完成后,我们就得到一棵带有样式的 DOM 树。也就是说,经过样式计算后,之前的 DOM 数和 CSSOM 数合并成了一颗带有样式的 DOM 树(render 树):
布局
render 树,只是用来描述页面的结构和样式的。接下来还需要通过布局(layout)来计算出每个节点的几何信息(geometry)。生成布局树的具体过程是:主线程会遍历刚刚构建的 render 树,根据 DOM 节点的计算样式计算出一个布局树(layout tree),布局树上每个节点会有它在页面上的 x,y 坐标以及盒子大小(bounding box sizes)的具体信息。布局树和 render 树大部分情况下并非一一对应,布局树只有那些可见的(visible)节点信息。例如 display:none 的节点不会生成到布局树上,伪元素选择器不存在 DOM 树上,但是在布局树上可见。
分层
为了确定哪些元素需要放置在哪一层,主线程需要遍历整颗布局树来创建一棵层次树(Layer Tree)。有了层次树后,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。可以通过谷歌浏览器开发者工具 layer 查看。
通常情况下,并不是 layout 树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。以下两种情况下渲染引擎会为特定的节点创建新图层:显式合成和隐式合成。
显示合成:拥有层叠上下文的节点,以下 css 属性会创建层叠上下文:
- HTML根元素本身就具有层叠上下文。
- 普通元素设置position不为static并且设置了z-index属性,会产生层叠上下文。
- 元素的 opacity 值不是 1。
- 元素的 transform 值不是 none。
- 元素的 filter 值不是 none。
- 元素的 isolation 值是isolate。
- will-change指定的属性值为上面任意一个。
除了层叠上下文外,需要剪裁(clip)的地方也会触发显示合成:比如一个标签很小,50*50像素,你在里面放了非常多的文字,那么超出的文字部分就需要被剪裁。当然如果出现了滚动条,那么滚动条也会被单独提升为一个图层。
隐式合成,通俗意义上来说,就是z-index比较低的节点会提升为一个单独的途图层,那么层叠等级比它高的节点都会成为一个独立的图层。
绘制指令
绘制指令,其实是主线程会为每个层单独产生绘制指令集(paint op
),用于描述这一层的内容该如何画出来。绘制指令可以理解为在某些坐标用什么颜色画一个矩形类似的意思。生成绘制指令集后,渲染主线程的工程就暂时告一段落,接下来主线程将每个图层的绘制信息交给合成线程来完成。
分层和绘制指令
分块
合成线程首先对每个图层进行分块,将其划分为更多的小区域(tile)。合成线程会从线程池中拿取多个线程来完成分块工作。
光栅化
有了图块之后,合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。更简单的理解就是确认每一个像素点的 rgb 信息,如下图所示:
光栅化的操作,并不由合成线程来做,而是会由合成线程将块信息交给 GPU 进程,以极高的速度完成光栅化。
绘制
当所有的图块都被栅格化后,合成线程会拿到每个层、每个块的位图,从而生成一个个「指引(quad)」信息。指引会标识出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转、缩放等变形。变形发生在合成线程,与渲染主线程无关,这就是 transform 效率高的本质原因。合成线程会通过 IPC 向浏览器进程(browser process)提交(commit)一个渲染帧。这个时候可能有另外一个合成帧被浏览器进程的 UI线程(UI thread)提交以改变浏览器的 UI。这些合成帧都会被发送给 GPU 完成最终的屏幕成像。如果合成线程收到页面滚动的事件,合成线程会构建另外一个合成帧发送给 GPU 来更新页面。
整体渲染流程总结如下:
参考 :