浏览器输入URL到页面渲染发生了什么及性能优化

228 阅读13分钟

浏览器在键入URL后会发生什么,今天来归整一下,对这个过程有一些大致的概念。

总体上,我们可以基本分为以下八个阶段

  1. 浏览器接收URL
  2. 查看缓存(浏览器缓存,系统缓存,路由器缓存),如果有直接访问,进行第8步
  3. DNS服务器进行域名解析,找到相对应的ip地址
  4. 通过ip地址找到对应服务器,进行TCP链接,完成三次握手,与服务器进行连接
  5. 浏览器向服务器发送http请求
  6. 服务器响应,将响应报文通过TCP发送回浏览器
  7. 关闭TCP连接,四次挥手
  8. 浏览器解析HTML、布局渲染

接下来,分阶段解析~

一、浏览器接收URL

     在平时使用时,我们也会发现,一般在浏览器中输入网址的时候,浏览器会从历史记录、书签等位置智能匹配可能的URL,然后智能提示,自动补全。甚至Chrome浏览器会从缓存中把网页展示出来,即还未确认URL页面就已经跳转出来。

**二、**查看缓存(浏览器缓存,系统缓存,路由器缓存),如果有直接访问,则直接进行第8步

具体说下浏览器缓存:浏览器缓存分为彻底缓存和缓存协商,详见上篇博文

在这里大体总结一下

2.1 强缓存的机制(http首部字段)

--Expires是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题:服务器时间与客户端时间可能不一致。因此该字段已经很少使用,现在基本用cache-control进行判断。
--cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。 如果同时存在cache-control和Expires,浏览器总是优先使用cache-control。

请求/响应指令)

no-cache,使用缓存前必须和服务器进行确认,也就是需要发起请求

no-store,不缓存

响应指令)

public,缓存文件保存在缓存服务器上,且其他用户也可以访问

private,只有特定用户才能访问该缓存资源

2.2 当缓存过期时,浏览器会向服务器发起请求询问资源是否真正过期,这就是缓存协商。对应http首部字段:last-modified,Etag

--last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送304状态码,让浏览器继续使用缓存。当然,用该方法也存在问题,比如修改时间有变化但实际内容没有变化,而服务器却再次将资源发送给浏览器。所以,使用Etag进行判断更好。
--Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,如果变化则返回新资源,否则返回304。

性能优化点:

问题:协商缓存过程需要发起HTTP请求,如果返回304,需要继续使用缓存。对于移动端一次请求还是有代价的,因此需要避免304

解决:a.对于很少进行更改的静态文件,可以在文件名中加入版本号,如test.v1.js;

          b.并且把Cache Control的max-age设置为一年半载,这样就不会发送请求

注意:当这些文件更新的时候,我们需要更新其版本号,这样浏览器才会到服务器下载新资源

**三、**如果没有,DNS服务器进行域名解析,解析成ip地址

3.1 什么是DNS?

DNS数据库是域名和IP地址相互映射的一个分布式数据库,DNS协议用来将域名转换为IP地址,它运行在UDP协议之上

3.2 详解具体步骤

① 请求发起后,游览器首先会解析这个域名,首先它会查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。

② 如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS(域名分布系统)服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。

③查询你输入的网址的DNS请求到达本地DNS服务器之后,本地DNS服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,此过程是递归的方式进行查询。如果没有,本地DNS服务器还要向DNS根服务器进行查询

④根DNS服务器没有记录具体的域名和IP地址的对应关系,而是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程

⑤本地DNS服务器继续向域服务器发出请求,在这个例子中,请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址

⑥最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。

3.3 性能优化点

域名解析这个环节有一个应对高并发的方法就是CDN,CDN 就是将静态的资源分发到位于多个地理位置机房中的服务器上,因此它能很好地解决数据就近访问的问题,也就加快了静态资源的访问速度。搭建CDN主要有两个关键点,

3.3.1是如何将用户的请求映射到 CDN 节点上

将用户的请求映射到 CDN 节点其实就是域名解析(DNS)的过程,DNS解析的过程大致如下:

>首先查看本机的host文件,查看是否有该域名对应的IP地址;

>如果没有就请求Local DNS是否有域名解析结果的缓存,如果有就返回标识是否从非权威DNS返回的结果;

>如果还是没有就进入DNS迭代查询的过程,先查询根DNS的地址(如.com/.cn/.org), 再请求顶级DNS得到二级域名服务器地址(如baidu.com);再从二级域名服务器中查询到 子域名对应的 IP 地址(如www.baidu.com),返回这个 IP 地址的同时标记这个结果是来自于权威 DNS 的结果,同时写入 Local DNS 的解析结果缓存,这样下一次的解析同一个域名就不需要做 DNS 的迭代查询了。

3.3.2是如何根据用户的地理位置信息选择到比较近的节点。

>根据用户的地理位置信息选择到比较近的节点则是GSLB的作用,GSLB(Global Server Load Balance,全局负载均衡)的含义是对于部署在不同地域的服务器之间做负载均衡,下面可能管理了很多的本地负载均衡组件。它有两方面的作用:一方面,它是一种负载均衡服务器,指的是让流量平均分配使得下面管理的服务器的负载更平均;另一方面,它还需要保证流量流经的服务器与流量源头在地缘上是比较接近的。

四、通过IP地址找到服务器,进行TCP链接,完成三次握手

在客户端发送数据之前会发起 TCP 三次握手用以同步客户端和服务端的序列号和确认号,并交换 TCP 窗口大小信息。

4.1三次握手的过程如下

  • 客户端发送一个带 SYN=1,Seq=X 的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
  • 服务器发回一个带 SYN=1, ACK=X+1, Seq=Y 的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
  • 客户端再回传一个带 ACK=Y+1, Seq=Z 的数据包,代表“握手结束”(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

4.2为什么需要三次握手?

    谢希仁著《计算机网络》中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

五、浏览器向服务器发送HTTP请求

   数据经过应用层、传输层、网络层、物理层逐层封装,传输到下一个目的地。
其中,每一层的作用如下。

  • 应用层:为应用进程提供服务,加应用层首部封装为协议数据单元。

  • 传输层:实现端到端通信,加TCP首部封装为数据包,TCP控制了数据包的发送序列的产生,不断的调整发送序列,实现流控和数据完整。

  • 网络层:转发分组并选择路由;加IP首部封装为IP分组。

  • 数据链路层:相邻的节点间的数据传输;加首部[mac地址]和尾部封装为帧。

  • 物理层:具体物理媒介中的数据传送,数据转换为比特流。

    下一个目的地接受到数据后,从物理层得到数据然后经过逐层的解包 到 链路层 到 网络层,然后开始上述的处理,在经网络层 链路层 物理层将数据封装好继续传往下一个地址。
到达最终目的地,再经过5层结构,逐层剥离,最终将数据送到目的主机的目的端口。

六、服务器响应,将响应报文通过TCP发送回浏览器

服务器操作系统将HTTP请求转发给WEB Server或者Application Server

  • Application进行业务逻辑处理并准备响应Response
  • Response准备完成,Response通过网卡回写到用户的浏览器(IO密集)

历经千辛万苦,终于将HTTP请求发送到正确的服务器,服务器怎么处理请求呢。这里基本是后端面试考察的重中之重了,其实后端从固定的端口接收TCP报文开始,会对连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,共上层使用。

       在大型网站中,还会将请求到反向代理服务器中,这是因为访问量巨大,一台服务器的性能已经不能满足需求,会很慢,需要将同一应用部署到多台服务器或集群上,把大量的用户请求分配出了,同时也提高了网站的稳定性和安全性,万一某台服务器宕机,并不会影响用户使用。这时候,客户端并非直接通过HTTP协议访问了网站的应用服务器,而是先请求到Nginx,然后Nginx在请求应用服务器,最后将结果返回给客户端,Nginx在这里的扮演的角色就是反向代理服务器。关于正向代理和反向代理,可以参见文章

      通过反向代理到达Web服务器,准确讲应该是HTTP服务器,常见的有Apache、Nginx、IIS等,服务器脚本会处理请求,获取所需的数据和内容。这里可以参考《Web服务器工作原理详解》

     Nginx是一个异步框架的 Web服务器,也可以用作反向代理,负载平衡器和HTTP缓存。Nginx可以在一台服务器上做反向代理的同时又做Web服务器,各自配置各自的即可。

七、关闭TCP链接

     为了避免服务器与客户端双方的资源占用和损耗,当双方没有请求或响应传递时,任意一方都可以发起关闭请求。与创建TCP连接的3次握手类似,关闭TCP连接,需要4次握手。

八、浏览器解析HTML、布局渲染

浏览器拿到响应文本 HTML 后,接下来介绍下浏览器渲染机制

浏览器解析渲染页面分为一下五个步骤:

  • 根据 HTML 解析出 DOM 树
  • 根据 CSS 解析生成 CSS 规则树
  • 结合 DOM 树和 CSS 规则树,生成渲染树
  • 根据渲染树计算每一个节点的信息
  • 根据计算好的信息绘制页面

8.1 根据HTML解析DOM树

  • 根据 HTML 的内容,将标签按照结构解析成为 DOM 树,DOM 树解析的过程是一个深度优先遍历。即先构建当前节点的所有子节点,再构建下一个兄弟节点。
  • 在读取 HTML 文档,构建 DOM 树的过程中,若遇到 script 标签,则 DOM 树的构建会暂停,直至脚本执行完毕。

8.2 根据CSS解析生成CSS规则树

  • 解析 CSS 规则树时 js 执行将暂停,直至 CSS 规则树就绪。
  • 浏览器在 CSS 规则树生成之前不会进行渲染。

8.3 结合DOM树和CSS规则树,生成渲染树

  • DOM 树和 CSS 规则树全部准备好了以后,浏览器才会开始构建渲染树。

  • 精简 CSS 并可以加快 CSS 规则树的构建,从而加快页面相应速度。

  • 所有我们知道:

    • CSS 会阻塞 JS 执行
    • JS 会阻塞后面的 DOM 解析

    为了避免这种情况,应该以下原则:

    • CSS 资源排在 JavaScript 资源前面
    • JS 放在 HTML 最底部,也就是 </body>

    另外,如果要改变阻塞模式,可以使用 defer 与 async,详见:这篇文章

8.4 根据渲染树计算每一个节点信息(布局)

  • 布局:通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸
  • 回流:在布局完成后,发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。

8.5 根据计算好的信息绘制页面

  • 绘制阶段,系统会遍历呈现树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。

  • 重绘:某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。

  • 回流:某个元素的尺寸发生了变化,则需重新计算渲染树,重新渲染。

  • 回流的成本要比重绘高很多,所以我们应该尽量避免产生回流。

    比如:

    • display:none 会触发回流,而 visibility:hidden 只会触发重绘。

完结~

喜欢请小伙伴动动手指留个赞哈