经典面试题:从输入URL到页面加载完成之间的过程。你会发现,这题不论大厂小厂,都会问,为什么?
因为它不仅可以考察面试者的知识广度还能考察面试者的知识深度。
前言
如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新的文章~
在上一篇文章这些浏览器面试题,看看你能回答几个?,我也提到过这个经典面试题。下面我们一起来看看吧~
说一说从输入URL到页面呈现发生了什么?(知识点)
这个题可以说是面试最常见也是一道可以无限难的题了,一般面试官出这道题就是为了考察你的前端知识的深度与广度。
1.浏览器接受URL开启网络请求线程(涉及到:浏览器机制,线程与进程等)
2.开启网络线程到发出一个完整的http请求(涉及到:DNS解析,TCP/IP请求,5层网络协议等)
3.从服务器接收到请求到对应后台接受到请求(涉及到:负载均衡,安全拦截,后台内部处理等)
4.后台与前台的http交互(涉及到:http头,响应码,报文结构,cookie等)
5.缓存问题(涉及到:http强缓存与协商缓存等)(请看上一篇文章这些浏览器面试题,看看你能回答几个?)
6.浏览器接受到http数据包后的解析流程(涉及到html词法分析,解析成DOM树,解析CSS生成CSSOM树,合并生成render渲染树。然后layout布局,painting渲染,复合图层合成,GPU绘制,等)
在浏览器地址栏输入URL
当我们在浏览器地址栏输入URL地址后,浏览器会开一个线程来对我们输入的URL进行解析处理。
浏览器中的各个进程及作用:(多进程)
- 浏览器进程:负责管理标签页的创建销毁以及页面的显示,资源下载等。
- 第三方插件进程:负责管理第三方插件。
- GPU进程:负责3D绘制与硬件加速(最多一个)。
- 渲染进程:负责页面文档解析(HTML,CSS,JS),执行与渲染。(可以有多个)
DNS域名解析
为什么需要DNS域名解析?
因为我们在浏览器中输入的URL通常是一个域名,并不会直接去输入IP地址(纯粹因为域名比IP好记),但我们的计算机并不认识域名,它只知道IP,所以就需要这一步操作将域名解析成IP。
URL组成部分
- protocol:协议头,比如http,https,ftp等;
- host:主机域名或者IP地址;
- port:端口号;
- path:目录路径;
- query:查询的参数;
- hash:#后边的hash值,用来定位某一个位置。
解析过程
-
首先会查看浏览器
DNS
缓存,有的话直接使用浏览器缓存 -
没有的话就查询计算机本地
DNS
缓存(localhost) -
还没有就询问递归式
DNS
服务器(就是网络提供商,一般这个服务器都会有自己的缓存) -
如果依然没有缓存,那就需要通过 根域名服务器 和
TLD
域名服务器 再到对应的 权威DNS
服务器 找记录,并缓存到 递归式服务器,然后 递归服务器 再将记录返回给本地
⚠️注意:
DNS解析是非常耗时的,如果页面中需要解析的域名过多,是非常影响页面性能的。考虑使用dns与加载或减少DNS解析进行优化。
发送HTTP请求
拿到了IP地址后,就可以发起HTTP请求了。HTTP请求的本质就是TCP/IP的请求构建。建立连接时需要3次握手进行验证,断开链接也同样需要4次挥手进行验证,保证传输的可靠性
。
3次握手
- 第一次握手:客户端发送位码为 SYN = 1(SYN 标志位置位),随机产生初始序列号 Seq = J 的数据包到服务器。服务器由 SYN = 1(置位)知道,客户端要求建立联机。
- 第二次握手:服务器收到请求后要确认联机信息,向客户端发送确认号Ack = (客户端的Seq +1,J+1),SYN = 1,ACK = 1(SYN,ACK 标志位置位),随机产生的序列号 Seq = K 的数据包。
- 第三次握手:客户端收到后检查 Ack 是否正确,即第一次发送的 Seq +1(J+1),以及位码ACK是否为1。若正确,客户端会再发送 Ack = (服务器端的Seq+1,K+1),ACK = 1,以及序号Seq为服务器确认号J 的确认包。服务器收到后确认之前发送的 Seq(K+1) 值与 ACK= 1 (ACK置位)则连接建立成功。
直白理解:
(客户端:hello,你是server么?服务端:hello,我是server,你是client么 客户端:yes,我是client 建立成功之后,接下来就是正式传输数据。)
4次挥手
- 客户端发送一个FIN Seq = M(FIN置位,序号为M)包,用来关闭客户端到服务器端的数据传送。
- 服务器端收到这个FIN,它发回一个ACK,确认序号Ack 为收到的序号M+1。
- 服务器端关闭与客户端的连接,发送一个FIN Seq = N 给客户端。
- 客户端发回ACK 报文确认,确认序号Ack 为收到的序号N+1。
直白理解:
(主动方:我已经关闭了向你那边的主动通道了,只能被动接收了 被动方:收到通道关闭的信息 被动方:那我也告诉你,我这边向你的主动通道也关闭了 主动方:最后收到数据,之后双方无法通信)
五层网络协议
1、应用层(DNS,HTTP):DNS解析成IP并发送http请求;
2、传输层(TCP,UDP):建立TCP连接(3次握手);
3、网络层(IP,ARP):IP寻址;
4、数据链路层(PPP):封装成帧;
5、物理层(利用物理介质传输比特流):物理传输(通过双绞线,电磁波等各种介质)。
OSI七层框架:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
服务器接收请求做出响应
HTTP 请求到达服务器,服务器进行对应的处理。 最后要把数据传给浏览器,也就是返回网络响应。
跟请求部分类似,网络响应具有三个部分:响应行、响应头和响应体。
响应完成之后怎么办?TCP 连接就断开了吗?
不一定。这时候要判断Connection
字段, 如果请求头或响应头中包含Connection: Keep-Alive
,
表示建立了持久连接,这样TCP连接会一直保持,之后请求统一站点的资源会复用这个连接。否则断开TCP连接, 请求-响应流程结束。
状态码
状态码是由3位数组成,第一个数字定义了响应的类别,且有五种可能取值:
- 1xx:指示信息–表示请求已接收,继续处理。
- 2xx:成功–表示请求已被成功接收、理解、接受。
- 3xx:重定向–要完成请求必须进行更进一步的操作。
- 4xx:客户端错误–请求有语法错误或请求无法实现。
- 5xx:服务器端错误–服务器未能实现合法的请求。
平时遇到比较常见的状态码有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500(分别表示什么请自行查找)。
服务器返回相应文件
请求成功后,服务器会返回相应的网页,浏览器接收到响应成功的报文后便开始下载网页,至此,网络通信结束。
浏览器解析渲染页面
浏览器在接收到HTML,CSS,JS文件之后,它是如何将页面渲染在屏幕上的?
解析HTML构建DOM Tree
浏览器在拿到服务器返回的网页之后,首先会根据顶部定义的DTD
类型进行对应的解析,解析过程将被交给内部的GUI渲染线程来处理。
DTD(Document Type Definition)文档类型定义
常见的文档类型定义
//HTML5文档定义
<!DOCTYPE html>
//用于XHTML 4.0 的严格型
<!DOCTYPE HTMLPUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
//用于XHTML 4.0 的过渡型
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
//用于XHTML 1.0 的严格型
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
//用于XHTML 1.0 的过渡型
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
HTML解释器的工作就是将网络或者本地磁盘获取的HTML网页或资源从字节流解释成DOM树
结构
通过上图可以清楚的了解这一过程:首先是字节流,经过解码之后是字符流,然后通过词法分析器会被解释成词语(Tokens),之后经过语法分析器构建成节点,最后这些节点被组建成一颗 DOM 树。
对于线程化的解释器,字符流后的整个解释、布局和渲染过程基本会交给一个单独的渲染线程来管理(不是绝对的)。由于 DOM 树只能在渲染线程上创建和访问,所以构建 DOM 树的过程只能在渲染线程中进行。但是,从字符串到词语这个阶段可以交给单独的线程来做,Chrome 浏览器使用的就是这个思想。在解释成词语之后,Webkit 会分批次将结果词语传递回渲染线程。
这个过程中,如果遇到的节点是 JS 代码,就会调用 JS引擎
对 JS代码进行解释执行,此时由于 JS引擎
和 GUI渲染线程
的互斥,GUI渲染线程
就会被挂起,渲染过程停止,如果 JS 代码的运行中对DOM树进行了修改,那么DOM的构建需要从新开始
如果节点需要依赖其他资源,图片/CSS等等,就会调用网络模块的资源加载器来加载它们,它们是异步的,不会阻塞当前DOM树的构建
如果遇到的是 JS 资源URL(没有标记异步),则需要停止当前DOM的构建,直到 JS 的资源加载并被 JS引擎
执行后才继续构建DOM
解析CSS构建CSSOM Tree
CSS解释器会将CSS文件解释成内部表示结构,生成CSS规则树,这个过程也是和DOM解析类似的,CSS
字节转换成字符,接着词法解析与法解析,最后构成 CSS对象模型(CSSOM)
的树结构
构建渲染树(Render Tree)
等DOM Tree
与CSSOM Tree
都构建完毕后,接着将它们合并成渲染树(Render Tree)
,渲染树
只包含渲染网页所需的节点,然后用于计算每个可见元素的布局,并输出给绘制流程,将像素渲染到屏幕上。
渲染(布局,绘制,合成)
- 计算CSS样式 ;
- 构建渲染树 ;
- 布局,主要定位坐标和大小,是否换行,各种
position overflow z-index
属性 ; - 绘制,将图像绘制出来。
这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain。
这里Reflow和Repaint的概念是有区别的:
(1)Reflow:即回流。一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树。
(2)Repaint:即重绘。意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就可以了。
回流的成本开销要高于重绘,而且一个节点的回流往往回导致子节点以及同级节点的回流, 所以优化方案中一般都包括,尽量避免回流。
回流一定导致重绘,但重绘不一定会导致回流
合成(composite)
最后一步合成( composite
),这一步骤浏览器会将各层信息发送给GPU,GPU将各层合成,显示在屏幕上
普通图层和复合图层
可以简单的这样理解,浏览器渲染的图层一般包含两大类:普通图层
以及复合图层
首先,普通文档流内可以理解为一个复合图层(这里称为默认复合层
,里面不管添加多少元素,其实都是在同一个复合图层中)
其次,absolute布局(fixed也一样),虽然可以脱离普通文档流,但它仍然属于默认复合层
。
然后,可以通过硬件加速
的方式,声明一个新的复合图层
,它会单独分配资源
(当然也会脱离普通文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层
里的回流重绘)
可以简单理解下:GPU中,各个复合图层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒
可以Chrome源码调试 -> More Tools -> Rendering -> Layer borders
中看到,黄色的就是复合图层信息。
推荐阅读
这些浏览器面试题,看看你能回答几个?
这一次带你彻底了解前端本地存储
面试官:说一说前端路由与后端路由的区别
JavaScript之原型与原型链
Javascript深入之作用域与闭包
this指向与call,apply,bind
觉得文章不错,可以点个在看呀_另外欢迎留言交流~