输入url到页面加载完成发生了什么谈跨域、缓存、DNS、http与https(版本,状态码,请求头)以及TCP/IP(三次握手四次挥手)
输入URL到页面加载完成发生了什么?这是一道非常经典的面试。这道题涉及的知识面很广,每一方面都能追得很深的细问,下面主要讲前端方面涵盖的知识点,想要更深的研究的小伙伴可以自行去了解。
- 浏览器输入URL按回车
- 检查有没有缓存,有缓存(http缓存)则返回200(form cache),否则继续
- DNS解析URL对应的IP
- 根据IP建立起TCP协议(三次握手)
- 客户端发起http请求
- 服务端处理请求,返回响应资源
- 浏览器接收响应资源(图片、文本、音频、视频等超文本数据);就html而言,浏览器解析构建DOM树,CSS树渲染页面
- 关闭TCP连接(四次握手)
一、URL
URL包括协议名、域名和端口号。其中http的默认端口号(一般会隐藏)为80,https的默认端口号为443。在这里可能会细问跨域,同源策略。协议,端口,域名 都相同时,则为同源,否则是跨域;html的script标签,css,img标签,iframe标签,可以请求第三方的资源(不受同源策略影响)
常见的解决跨域的方式有:
1. JSONP(动态创建script标签)
原理:通过script标签不受同源策略影响的特点,使用 script 跨域请求一段字符串,来执行一个定义好的函数,将请求的数据作为参数回传。
JQuery使用jsonp:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
<script src="http://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js"></script>
</head>
<body>
<div id="divCustomers"></div>
<script>
$.getJSON("http://localhost/cors?jsoncallback=?", function(data) {
var html = '<ul>';
for(var i = 0; i < data.length; i++){
html += '<li>' + data[i] + '</li>';
}
html += '</ul>';
$('#divCustomers').html(html);
});
</script>
</body>
</html>
特点:
- 只支持get,不支持post
- 调用失败的时候没有对应的HTTP状态码
- 安全性不好
2. CORS策略:自定义http首部信息,一般服务器设置Access-Control-Allow-Origin(允许哪些域名访问)就可以实现跨域;前端改动的比较少,主要需要服务端支持该协议。讲到Access-Control-Allow-Origin的同时,也会用到Access-Control-Allow-Credentials:true(允许客户端发送cookie)同时客户端需要设置xhr.withCredentials = true。
cookie设置小结
要想通过客户端发送cookie给服务器端,必须有服务器端支持。
Access-Control-Allow-Credentials必须为true且只能为true
Access-Control-Allow-Origin必须指定且不能为'*'
客户端需要指定xhr.withCredentials = true;
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
cors包含简单请求和非简单请求两种
- 简单请求
条件:
1. 请求方法为 HEAD GET POST 三种之一;
2. HTTP请求头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
请求过程:- 浏览器直接发出cors请求,自动在头信息中增加Origin字段,如 Origin: http://m.siky.top
- 服务端根据Origin字段拿到请求源
如果请求源在许可范围,返回的请求头中就会包含 Access-Control-Allow-Origin 字段
如果请求源不在许可范围,服务器正常返回http响应 - 浏览器收到响应,判断是否有 Access-Control-Allow-Origin 字段
若有则知道跨域请求成功,正常返回数据
若无则跟普通跨域请求一样,报错 No ‘Access-Control-Allow-Origin’ ,无返回数据 - 服务端返回浏览器端的响应头:
Access-Control-Allow-Origin 允许跨域的Origin列表
Access-Control-Allow-Methods 允许跨域的方法列表(GET、POST等)
Access-Control-Allow-Headers 允许跨域的Header列表
Access-Control-Expose-Headers 允许暴露给JavaScript代码的Header列表
Access-Control-Max-Age 最大的浏览器缓存时间,单位为s
2.非简单请求:非简单请求,会在正式通信之前,通过 OPTIONS 请求方法发送一次"预检"请求。
服务器代理:webpack的proxy代理和服务器的nginx代理
关于这个nginx反向代理服务器的相关配置可以去了解一下,nginx反向代理+负载均衡+动静态代理服务器
websocket全双工通信,客户端,服务端这两端都可以主动向对方建立连接,一旦连接建立起来,就可以真正的平等的进行数据交流。这个协议不受http协议的影响,但也需要服务器支持此协议。详情可见:WebSocket 教程 - 阮一峰的网络日志
二、缓存
就浏览器而言,一般缓存我们分为四类,按浏览器读取优先级顺序依次为:Memory Cache、Service Worker Cache、HTTP Cache、Push Cache。而这里主要讲的就是 HTTP Cache ,其他有兴趣可以自行搜索。
http是开发中应用最多的缓存,其中包括强缓存和协商缓存
强缓存:直接从本地副本比对读取,不去请求服务器,返回的状态码是 200。
协商缓存:会去服务器比对,若没改变才直接读取本地缓存,返回的状态码是 304。
强缓存主要包括 expires、pragrma 和 cache-control。
- expires:
HTTP1.0中定义的缓存字段,
请求资源时,服务器返回的响应头Response Headers中增加了expires字段表示资源过期的时间戳,如:
expires: Thu, 03 Jan 2019 11:43:04 GMT
当客户端再去请求时,就会用客户端的时间与该时间戳进行对比,若小于该时间戳则用缓存资源,否则进行http请求。
缺点:
1. 客户端时间与服务器时间有偏差
2. 客户端的时间(也就是浏览器的系统时间)是可以通过人为进行修改的。
- cache-control:
HTTP1.1 中定义的字段,可解决 expires 的缺点,优先级大于 expires
cache-control: public, max-age=3600, s-maxage=3600
(1) max-age / s-maxage
值为数字,表示缓存在多少秒之后失效,如果未失效则使用缓存,返回200 from cache,若失效了想服务器请求资源,返回200 ok
浏览器中都起作用,优先级 s-maxage 大于 max-age
代理服务器中 s-maxage 起作用
可以通过在 Request Header 中设置 max-age=0 来表示资源过期,进而直接向服务器请求资源
(2) public / private
public 表示该资源可被客户端和代理服务器缓存
private 表示该资源只能被客户端缓存
默认值为 private ,当未设置 private 且设置了 s-maxage 的时候,表示允许代理服务器缓存,相当于 public
(3) no-cache / no-store
no-cache 表示不询问浏览器缓存情况,而是向服务器验证当前资源是否需要更新,是直接使用了协商缓存,若无更新则使用缓存,返回304,否则200 ok
no-store 则表示不使用缓存策略,直接向服务器请求最新资源
优先级最高,使用 no-cache / no-store 时,不考虑 max-age 和 s-maxage
- pragma:
pragma: no-cache
值有no-cache/no-store,意思和cache-controll的一样,三者同时出现时的优先级为:pragma->cache-controll->expires
协商缓存主要包括last-modified和Etag。
- last-modified
响应头中增加字段 last-modified ,记录资源最后修改时间,如
last-modified: Fri, 16 Jul 2021 06:22:32 GMT
当后续再次请求该资源时,请求头增加字段 if-modified-since 字段,值为之前返回的 last-modified 字段的值
服务端对比该最后修改时间,若一致则证明未修改,告知浏览器使用缓存,并返回304,否则返回更新的资源,并修改 last-modified 的为新时间
缺点:
1. 时间精度只能精确到秒
2. 以修改时间为依据,而不是以内容是否改变为依据
- Etag:
etag是基于资源的内容编码生成的一串唯一标识字符串,内容不同则etag不同,解决了 last-modified 无法检测内容改变的缺点
资源响应头中包含 etag 字段,值为唯一标识字符串,如
etag: "FllOiaIvA1f-ftHGziLgMIMVkVw_"
再次请求该资源时,请求头上增加字段 if-no-match 字段,值为之前返回的 etag 字段的值
服务端对比该标识字符串,若一致则表明资源未更新,缓存可用,返回304,浏览器使用缓存;若不一致则返回新的资源,并修改 etag 字段为新值
优点:
精确感知资源的内容变化
缺点:
生成标识字符串增加了服务器的开销
访问刷新分析
-
标签进入、输入url回车进入
(1) 先检查强缓存
a. 若有 cache-control
若有 no-store,则不使用缓存,向服务器请求最新资源,返回 200 ok
若有 no-cache,则不使用缓存,向服务器协商缓存,未过期则使用缓存并返回 304,过期则返回新资源 200
检查失效时间 max-age / s-maxage ,若未失效则使用缓存,返回 200 from chache
b. 若无 cache-control 则 检查 expires ,检查失效时间戳是否到期,未失效则使用缓存,返回 200 from cache(2) 若本地缓存已经过期,则使用协商缓存路线 根据 last-modified / etag 检查资源是否过期,若未过期则使用缓存,返回304 not modified (3) 若强缓存和协商缓存都过期,则返回新资源,状态码200 ok,并响应头中更新协商缓存的失效时间
-
按刷新按钮、F5 刷新、网页右键“重新加载”
浏览器会将资源请求头设置为 cache-control: max-age=0 来使缓存过期,走协商缓存路线,向服务器检查资源是否过期 -
ctrl + F5 强制刷新
将请求头设置 no-cache ,强制获取最新的资源,发送请求头如下
cache-control: no-cache
pragma: no-cache
三、DNS域名解析
解析出映射的IP地址,浏览器缓存 -> 操作系统缓存 -> 本地DNS缓存 递归查询
本地DNS服务器 -> 根DNS服务器 -> *DNS服务器 -> 权威DNS服务器 迭代查询
四、建立TCP连接(三次握手)
鉴于好多面试都是三次握手,四次挥手一起问的,所以关闭连接的四次挥手也在这里一起讲了。TCP的三次握手、四次挥手
序列号seq: 占4个字节,用来标记数据断的顺序。TCP把连接发送的数据字节都会编上一个序列号,seq就是这段数据报文的第一个字节的序列号。
确认号ack:占4个字节,期待收到对方下一数据报文的第一个字节的序列号。
确认包ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效。
同步包SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
终止包FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接
PS:ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号。
三次握手过程理解
第一次握手:建立连接时,客户端发送SYN包(seq=x)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手:服务器收到SYN包,必须确认客户的ACK包(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
四次挥手过程理解
第一次挥手:客户端发送释放连接报文FIN=1,并停止发送数据。释放数据报文首部,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)此时,客户端进入终止等待1的状态
第二次挥手: 服务端接受到释放连接报文后,发送确认包ACK(序号ack=u+1)并带上自己的序列号seq=v;此时,服务端进入关闭等待状态;客户端接受到确认请求后,进入终止等待2状态。
第三次挥手:服务端将最后的数据发送完之后,服务端发送释放连接报文FIN=1,并携带确认包ACK=1,ack=u+1;由于在半关闭的状态,可能还会发送一些数据,这里假设是seq=w;此时,服务端进入最后确认状态。
第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。等待2MSL后进入CLOSED阶段,服务器接收到最后确认包之后,也进入CLOSED阶段,(服务器的关闭比客户端的早)连接关闭。
ps:为何握手三次,挥手四次?(简单说:释放连接需要时间准备)
五、浏览器发起http请求
这里要详细讲讲http的消息结构以及协议、版本之间的区别。关于http相关的知识,是前端人员必须掌握的,也是重点之一。
- http协议是什么?
超文本传输协议,是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。 - http和https的区别
- http版本区别
- http消息结构
http请求报文由三部分组成:请求行、请求头、请求主体
- 请求行包括请求方法、请求URL、协议名称以及版本号;
- 请求头就是报文头若干个属性(下面会详细列出),如Cache-Control、referer
- 请求主体就是URL携带的参数,param1=value1¶m2=value2
http响应报文包括响应行、响应头、响应体
- 响应行包括协议名称、版本号、状态码以及状态描述。
- 响应头就是报文头若干个属性(下面会详细列出),如Etag、cache-controll、Set-Cookie
- 响应主体就是服务器返回的数据
- http首部
通用首部 :请求报文和响应报文两方都会使用的首部。
每个属性具体详解
请求首部:也是请求头;用于说明是谁或什么在发送请求、请求源自何处,或者客户端的洗好及能力。服务器可以根据请求首部给出的客户端信息,试着为客户端提供更好的响应。每个属性具体详解
响应首部:也是响应头;用于补充响应的附加信息、服务器信息,以及对客户端的附加要求等信息。这些首部有助于客户端处理响应,并在将来发起更好的请求。每个属性的具体详解
实体首部:针对请求报文和响应报文的实体部分使用的首部。能够对资源使用的各种有效的请求方法。总之,实体首部可以告知报文的接受者它在对什么进行处理。每个属性具体详解
- 响应状态码:状态码详情
六、HTML页面渲染
1. Browser进程下载html文件并将文件发送给renderer进程
2. renderer进程的GUI进程开始解析html文件来构建出DOM
3. 当遇到外源css时,Browser进程下载该css文件并发送回来,GUI线程再解析该文件,在这同时,html的解析也同时进行,但不会渲染(还未形成渲染树)
4. 当遇到内部css时,html的解析和css的解析同时进行
5. 继续解析html文件,当遇到外源js时,Browser进程下载该js文件并发送回来,此时,js引擎线程解析并执行js,因为GUI线程和js引擎线程互斥,所以GUI线程被挂起,停止继续解析html。直到js引擎线程空闲,GUI线程继续解析html。
6. 遇到内部js也是同理
7. 解析完html文件,形成了完整的DOM树,也解析完了css,形成了完整的CSSOM树,两者结合形成了render树
8. 根据render树来进行布局,若在布局的过程中发生了元素尺寸、位置、隐藏的变化或增加、删除元素时,则进行回流,修改
9. 根据render树进行绘制,若在布局的过程中元素的外观发生变换,则进行重绘
10. 将布局、绘制得到的各个简单图层的位图发送给Browser进程,由它来合并简单图层为复合图层,从而显示到页面上
11. 以上步骤就是html文件解析全过程,完成之后,如若当页面有元素的尺寸、大小、隐藏有变化时,重新布局计算回流,并修改页面中所有受影响的部分,如若当页面有元素的外观发生变化时,重绘
PS:更深的可自行去了解浏览器运行机制。
本文地址:https://blog.csdn.net/qq_31392495/article/details/109217295
上一篇: Python使用sys.exc_info()方法获取异常信息
下一篇: 样式穿透浅解笔记