前端面试经典题之从输入地址到页面加载过程中发生了什么

  从用户输入地址到浏览器加载界面,主要过程如下:

  1. DNS解析
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器接收请求并响应
  5. 浏览器解析并进行渲染
  6. 连接结束

 下面进行具体过程分析

  一、 DNS解析

  通俗地来讲,DNS解析就是讲用户输入的站点地址解析成服务器所在的ip地址。用户在浏览器中输入的地址并不是该域名真正意义上的地址,而要将该地址(譬如www.baidu.com)解析成ip地址的过程,就是DNS解析。

  DNS解析是一个递归查询的过程,这个过程所需要的步骤有点繁杂(有兴趣可自行百度查询),而且存在着多次TCP和UDP请求,十分占用资源,所以这就涉及到DNS的一个知识点 - DNS优化

  DNS优化方法之一是使用DNS缓存,一般可分为:浏览器缓存、系统缓存、路由器缓存、IPS缓存、跟域名缓存、顶级域名缓存和主域名服务器缓存。

  DNS优化的第二种方法是DNS负载均衡,我们平时所说的CDN(Content Delivery Network)内容分发网络,使用到的也是DNS负载均衡技术。这个过程中使用多台服务器来提供响应,它的原理是当接收到用户发送过来的请求时,可以返回一个最适合用户的IP地址给用户(根据用户的地理位置或其他影响传输效率的因素分配的),这个过程就是DNS负载均衡,也叫做DNS重定向。

  二、 TCP连接

  通过DNS域名解析后就可以获取到服务器的地址了,接下来就是客户端要与服务器建立连接,这个过程是由TCP协议完成的,也就是我们所熟悉的三次握手。

  

  第一次握手: 建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认; 

  第二次握手: 服务器收到客户端发过来的syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),此时服务器进入SYN_RECV状态;

  第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

  在查阅资料的时候看到一个图,似乎可以帮助我们更加轻易地了解三次握手的过程,大家可以看一看:

  

  

  当三次握手完成后便TCP连接便完成了,客户端可以与服务器进行传输数据。

  关于三次挥手,这位博主使用了更加形象并且贴近生活的例子来讲解,也可以参考一下他的文章来帮助理解:https://www.cnblogs.com/lms0755/p/9053119.html

  三、发送HTTP请求

  在客户端与服务器连接成功之后,客户端要先发送一个请求给服务器,告知服务器我要做什么事,需要调用什么资源。一个完整的HTTP请求报文包含三个部分,分别是请求行、请求报头、请求正文。举个栗子:

1 GET/sample.jspHTTP/1.1
2 Accept:image/gif.image/jpeg,*/*
3 Accept-Language:zh-cn
4 Connection:Keep-Alive
5 Host:localhost
6 User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
7 Accept-Encoding:gzip,deflate
8 
9 username=jinqiao&password=1234

  第1行为请求行,它包含了请求方法(GET),URL(sample.jsp),http版本(HTTP/1.1)。其中,常见的请求方法由GET、POST、PUT、DELETE、OPTIONS、HEAD。

  第2-7行为请求报头,它包括了Accept(指定客户端用于接受哪些类型的信息)、Accept-Language(指定可接受语言种类)、Accept-Encoding(指定接受的编码方式)、Connection(设置为Keep-alive可用于告诉客户端本次HTTP请求结束之后并不需要关闭TCP连接,方便下次继续使用HTTP请求时使用相同的TCP通道,提高效率)、Host(客户端指定自己想访问的http服务器的域名/IP 地址和端口号)、User-Agent(向访问网站提供你所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识)等等。

  第8行为空行,表示请求报头在这里就结束了,这个空行不能省略!

  第9行是请求正文,当用户发送请求时需要将自己的数据发送给服务器,这些数据就存储在请求正文当中。

  四、服务器接收请求并作出响应

  与请求报文类似,响应报文也分为三个部分,分别是状态码、响应头部、响应正文。

  状态码主要有以下几种:

  1xx:指示信息–表示请求已接收,继续处理。

  2xx:成功–表示请求已被成功接收、理解、接受。

  3xx:重定向–要完成请求必须进行更进一步的操作。

  4xx:客户端错误–请求有语法错误或请求无法实现。

  5xx:服务器端错误–服务器未能实现合法的请求。

  响应头部主要由Cache-Control、 Connection、Date、Pragma等组成。

  响应体为服务器返回给浏览器的信息,主要由HTML,css,js,图片文件组成。

  五、浏览器进行解析并渲染

  客户端在接收服务器传输过来的文件之后,便由游览器的引擎开始解析文件并在屏幕中渲染出来,其步骤主要如下:

  1. 接收字节,处理HTML并构建出DOM树;

  2. 处理CSS文件并构建出CSSOM树;

  3. 将DOM树和CSSOM树结合起来并构成渲染树;

  4. 根据渲染树来布局,并计算出各个元素的几何信息;

  5. 在屏幕中渲染像素,并绘制节点。

  详细解析过程可参考:https://blog.csdn.net/sunyuan_software/article/details/50618891,浏览器渲染流程分析与总结。

  浏览器还没接收到完整的HTML文件时,它就开始渲染页面了,在遇到外部链入的脚本标签或样式标签或图片时,会再次发送HTTP请求重复上述的步骤。在收到CSS文件后会对已经渲染的页面重新渲染,加入它们应有的样式,图片文件加载完立刻显示在相应位置。在这一过程中可能会触发页面的重绘或重排。这里就涉及了两个重要概念:Reflow(回流)和Repaint(重绘)。

  首先我们需要了解一下什么是回流和重绘:

  回流,在元素的内容、结构、位置或尺寸等发生变化时,需要重新计算它的样式和重构渲染树,这个过程就叫reflow,也叫做layout。

  重绘,当元素仅发生外观上的改变(如背景色、边框颜色、文字颜色等)时,只需要应用新的样式而不需要修改其CSSOM树和DOM树,这个过程就叫做repaint。

  通过它们的定义我们可以知道,回流的成本要比重绘高得多,所以在实际开发过程中,我们应尽量避免回流的发生,因为DOM树里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。

  下面列举出一些可能会出现回流/重绘的操作:

  • 增加、删除、修改DOM结点时,会导致回流或重绘;
  • 移动DOM的位置,或是做特效动画的时候;
  • 内容发生变化; 
  • 修改CSS样式的时候;
  • Resize窗口的时候,或是滚动的时候;
  • 修改网页的默认字体时;

  基本上来说,reflow有如下的几个原因:

  • Initial,网页初始化的时候;
  • Incremental,一些js在操作DOM树时;
  • Resize,其些元件的尺寸变了;
  • StyleChange,如果CSS的属性发生变化了;
  • Dirty,几个Incremental的reflow发生在同一个frame的子树上;

  小拓展:如果使用传统的方式来修改样式,必定会多次操作DOM树导致资源占用率较高。而vue因为使用了虚拟DOM树,它是在虚拟DOM树上模拟做出了所有修改操作之后,再一次性更新到DOM树上,所以可以有效地提升效率,避免了多次回流/重绘。

  讲完html解析,下面讲一下js的解析。

  js的解析是由浏览器中的js解析引擎来完成的。我们都知道js是单线程运行的,所有任务必须排队来完成。但有时候又可能会有一些任务比较耗时,所以需要一种机制可以先执行排在后面的任务。所以为了解决这种情况引入了一种概念:异步操作(asynchronous)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。关于异步操作,我会在后面写一篇详细博文来介绍。

  浏览器在解析过程中,如果遇到请求外部资源时,如图像、iconfont、JS等,浏览器将会重复上述过程下载资源。这些请求过程是异步的,并不会影响HTML文档进行加载;但是当文档加载过程中遇到JS、HTML文件时浏览器就会挂起渲染程序,要等到文档中JS文件加载完毕并等待解析执行完成,才会继续HTML的渲染过程。其原因是JS有可能修改DOM结构,这就意味着JS执行完成前,后续所有资源的下载是没有必要的,这就是JS阻塞后续资源下载的根本原因。CSS文件的加载不影响JS文件的加载,但是却影响JS文件的执行,JS代码执行前浏览器必须保证CSS文件已经下载并加载完毕。

  六、 连接结束

  当客户端从服务器那接收完所有数据之后,便会向服务器发送一个信号,请求与服务器断开连接,这就是我们常说的四次挥手过程。

  它的主要流程如下图: 

  

 

  

  第一次挥手是浏览器发完数据后,发送FIN给服务器请求断开连接;

  第二次挥手是当服务器接收到客户端的FIN时,向客户端发送一个ACK,其中ACK的值等于FIN+seq;

  第三次挥手是服务器向客户端再发送一个FIN,告知客户端应用程序已关闭;

  第四次挥手是当客户端收到服务器发送过来的FIN时,回复一个ACK给服务器,其中ACK的值是FIN+seq。

  小拓展1:为什么要进行4次挥手?

  最简明扼要的答案就是:为了确保数据能够被完整传输。当被动方(在本文中可理解为服务器)接收到请求断开连接的一方(在本文中可理解为客户端,即浏览器)传输过来的FIN报文通知时,它仅仅表示主动方没有数据再发送给被动方了。但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET,它可能还需要发送一些数据给主动方后,再发送FIN报文给主动方,告诉主动方同意关闭连接,所以这里的ACK报文和FIN报文多数情况下都是分开发送的。

  小拓展2:为什么连接的时候是三次握手,关闭的时候却是四次握手?

  因为当服务器收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

  总结:

  至此,浏览器从输入url地址,到界面完成呈现的过程便分析得差不多了,其中包含了很多知识点,十分考验应试者的知识面,需要花时间将这个过程消化下来。这也是我的第一篇博客,参考了前人们得出来的经验,并稍作总结,希望能够帮助到像我一样正在准备面试的小伙伴们。

 

  参考资料:

  理解TCP/IP三次握手与三次挥手的正确姿势:https://www.cnblogs.com/lms0755/p/9053119.html

  TCP的三次握手与四次挥手理解及面试题:https://blog.csdn.net/qq_38950316/article/details/81087809

  浏览器渲染流程分析与总结:https://blog.csdn.net/sunyuan_software/article/details/50618891

  前端经典面试题:从输入URl到页面加载发生了什么:https://segmentfault.com/a/1190000006879700

  

  

 

posted @ 2019-08-06 00:39 卑微小陈的随笔 阅读(...) 评论(...) 编辑 收藏