.jpg)
# 1.解析输入的URL地址
传输协议(把信息在客户端和服务器端进行传递,类似于快递小哥)
- http 超文本传输协议(传输的内容除了文本,还有可能是其它类型:二进制编码、BASE64码、文件流等等)
- https 比HTTP更加安全的传输协议(传输通道设置加密算法SSL),一般支付类网站都是HTTPS协议
- ftp 资源上传协议,一般应用于把本地文件直接上传到服务器端
域名 zhufengpeixun.cn
- 一级域名
- 二级域名
- 三级域名
端口号 (根据端口号,找到当前服务器上指定的服务)
- 0~65535之间
- 不同协议有自己默认的端口号(也就是自己不用写,浏览器会帮我们加上)
- http => 80
- https => 443
- ftp => 21
- 除这几个在书写的时候可以省略,其余的不能省
请求资源的路径和名称
- /stu/index.html
- 一般情况下,如果我们访问的是index.html等,可以省略不写(因为服务端一般会设置index.html为默认文档,当然可以自定义)
- 伪URL
- SEO优化 https://item.jd.com/100006038463.html
- 数据请求的接口地址 /user/list
- /stu/index.html
问号传参部分 ?xxx=xxx
客户端基于GET系列请求,把信息传递会服务器,一般都会基于问号传参的模式
页面之间跳转,信息的一些通信也可以基于问号传参的方式(单页面中组件和组件跳转之间的信息通信,也可能基于问号传参)
# 关于传递的内容需要进行编码处理(处理特殊字符和中文)
客户端和服务器端都支持这两种编码和解码方式(客户端和服务器端信息编码一般都是基于这种方式)
encodeURI / decodeURI 只能把空格和中文内容进行编码和解码,所以一般应用这种模式处理整个URL的编码
encodeURIComponent / decodeURIComponent 会把所有的特殊字符和汉字都进行编码,一般不会整个URL编码,只会给传递的每一个参数值单独编码
escape / unescape 这种方式不一定所有的后台都有,所以一般只应用于客户端自己内部编码,例如:存储cookie信息,把存储的中文进行编码和解码;特殊符号也会被编码;
...
设置哈希HASH #xxx
# 2.DNS解析
网站中,每发送一个TCP请求,都要进行DNS解析(一但当前域名解析过一次,浏览器一般会缓存解析记录,缓存时间一般在1分钟左右,后期发送的请求如果还是这个域名,则跳过解析步骤 =>这是一个性能优化点)
真实项目中,一个大型网站,他要请求的资源是分散到不同的服务器上的(每一个服务器都有自己的一个域名解析)
- WEB服务器(处理静态资源文件,例如:html/css/js等 的请求)
- 数据服务器(处理数据请求)
- 图片服务器 (处理图片请求)
- 音视频服务器
- ......
这样导致,我们需要解析的DNS会有很多次
优化技巧:DNS Prefetch 即 DNS 预获取
让页面加载(尤其是后期资源的加载)更顺畅更快一些
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//static.360buyimg.com">
<link rel="dns-prefetch" href="//misc.360buyimg.com">
<link rel="dns-prefetch" href="//img10.360buyimg.com">
<link rel="dns-prefetch" href="//img11.360buyimg.com">
<link rel="dns-prefetch" href="//img12.360buyimg.com">
.......
# 3.基于TCP的三次握手,够建客户端和服务器端的连接通道
只有建立好连接通道,才能基于HTTP等传输协议,实现客户端和服务器端的信息交互

- 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
- 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
- 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击
# 4.发送HTTP请求
基于HTTP等传输协议,客户端把一些信息传递给服务器
HTTP请求报文(所有客户端传递给服务器的内容,统称为请求报文)
- 谷歌控制台NetWork中可以看到
- 请求起始行
- 请求首部(请求头)
- 请求主体
- Host Content-Length Connection Content-Type Content-Encoding
HTTP状态码
1 开头的代表处理中,一般见不到
2开头:都是成功
- 200:OK:成功
- 201:CREATED:一般应用于告诉服务器创建一个新文件,最后服务器创建成功后返回的状态码
- 204:NO CONTENT:对于某些请求(例如:PUT或者DELETE),服务器不想处理,可以返回空内容,并且用204状态码告知
- 「206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态
3开头:代表成功,只不过中间需要中转一下
- 301:Moved Permanently:永久重定向(永久转移)
- 302:Moved Temporarily:临时转移,很早以前基本上用302来做,但是现在主要用307来处理这个事情,
- 307的意思就是临时重定向Temporary Redirect =>主要用于:服务器的负载均衡等
- 304:Not Modified:设置HTTP的协商缓存
4开头:都是失败
- 400:Bad Request:传递给服务器的参数错误
- 401:Unauthorized:无权限访问,表示认证错误
- 404:Not Found:请求地址错误
- 「403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。收到403响应表示服务器完成认证过程,但是客户端请求没有权限去访问要求的资源
5开头:都是失败
- 500:Internal Server Error:未知服务器错误
- 「501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
- 「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误
- 503:Service Unavailable:服务器超负荷
强缓存 和 协商缓存(性能优化:减少HTTP请求的次数)
- 强缓存 ( Cache-Control 和 Expires )
- 协商缓存 ( Last-Modified 和 Etag )
HTTP的请求方式
GET 与 POST 系列请求的区别(所谓的区别都是约定俗成的,并没有一定要这样做的规定)
GET 系列请求:一般用于从服务器获取信息(GET:给的少,拿的多)
- GET
- DELETE:一般应用于告诉服务器,从服务器上删除点东西
- HEAD:只想获取响应头内容,告诉服务器响应主体内容不要了
- OPTIONS:试探性请求,发个请求给服务器,看看服务器能不能接收到,能不能返回
POST 系列请求:一般用于给服务器推送信息(POST:给的多,拿的少)
- POST
- PUT:和DELETE对应,一般是想让服务器把我传递的信息存储到服务器上(一般应用于文件和大型数据内容)
本质区别
GET系列传递给服务器信息的方式一般采用:问号传参
POST系列传递给服务器信息的方式一般采用:设置请求主体
本质区别所导致的问题
- GET传递给服务器的内容比POST少,因为URL有最长大小限制
GET:xhr.open('GET','/list?name=xxx&year=xxx&xxx=xxx...')
IE浏览器一般限制2KB,谷歌浏览器一般限制4~8KB,超过长度的部分自动被浏览器截取了
POST:xhr.send('....')
请求主体中传递的内容理论上没有大小限制,但是项目中,为了保证传输的速度,我们会自己限制一些
- GET会产生缓存(缓存不是自己可控制的)
因为请求的地址(尤其是问号传递的信息一样),浏览器有时候会认为你要和上次请求的数据一样,拿的是上一次信息;
这种缓存我们不期望有,我们期望的缓存是自己可控制的;所以项目中,如果一个地址,GET请求多次,我们要去除这个缓存;
解决办法:设置随机数
xhr.open('GET','/list?name=xxxx&_='+Math.random());
- GET相比较POST来说不安全(只是相对他俩来说)
GET是基于问号传参传递给服务器内容,有一种技术叫做URL劫持,这样别人可以获取或者篡改传递的信息; 而POST基于请求主体传递信息,不容易被劫持;
GET 和 POST 方法都是安全和幂等的吗?
在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的
那么很明显 GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。
POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的
# 5.服务器接受到请求,并进行处理,最后把信息返回给客户端
- HTTP响应报文(所有服务器返回给客户端的内容)
- 响应起始行
- 响应首部(响应头)
- date存储的是服务器的时间
- ...
- 响应主体
- 服务器返回的时候是:先把响应头信息返回,然后继续返回响应主体中的内容(需要的信息大部分都是基于响应主体返回的)
# 6.断开TCP 四次挥手
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此
- 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
- 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
- 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
- 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
为什么“握手”是三次,“挥手”却要四次
TCP建立连接时之所以只需要"三次握手",是因为在第二次"握手"过程中,服务器端发送给客户端的TCP报文是以SYN与ACK作为标志位的。SYN是请求连接标志,表示服务器端同意建立连接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文。
即SYN建立连接报文与ACK确认接收报文是在同一次"握手"当中传输的,所以"三次握手"不多也不少,正好让双方明确彼此信息互通。
TCP释放连接时之所以需要“四次挥手”,是因为FIN释放连接报文与ACK确认接收报文是分别由第二次和第三次"握手"传输的。为何建立连接时一起传输,释放连接时却要分开传输?
- 建立连接时,被动方服务器端结束CLOSED阶段进入“握手”阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。
- 释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,因为还有必要的数据需要处理,所以服务器先返回ACK确认收到报文,经过CLOSE-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。
为什么客户端在TIME-WAIT阶段要等2MSL?
为的是确认服务器端是否收到客户端发出的ACK确认报文
当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。
服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;
- 如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;
- 否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。
Connection: Keep-Alive 保持TCP不中断(性能优化点,减少每一次请求还需要重新建立链接通道的时间)
# 7.客户端渲染服务器返回的结果
# 前端性能优化点
# 1. 减少HTTP请求的次数和大小
- 合并压缩 webpack(代码比较少的情况下,尽可能使用内嵌式)
- 雪碧图或者图片BASE64
- 对于动态获取的图片,采用图片懒加载(数据也做异步分批加载:开始只请求加载第一屏的数据,滑动到第几屏在加载这一屏的数据和图片)
- 骨架屏技术(首屏内容由服务器渲染;再或者开始展示占位结构,客户端在单独获取数据渲染;)
- 音视频取消预加载(播放的时候再去加载音视频文件,对于自动播放采取延迟播放的处理)
- 服务器采用GZIP压缩
- ....
# 2.建立缓存机制
把一些请求回来的信息进行本地存储(缓存存储),在缓存有效期内,再次请求资源,直接从缓存中获取数据,而不是服务器上从新拉取
- DNS预获取
- 资源文件的强缓存和协商缓存(304)
- 数据也可以做缓存(把从服务器获取的数据存储到本地:cookie/localStorage/redux/vuex等,设定期限,在期限内,直接从本地获取数据即可)
- 离线存储(一般很少用)manifest
- CDN区域分布式服务器开发部署(费钱 效果会非常的好)
- ....
# 3.代码上的优化
- 减少DOM的重绘和回流
- 在JS中尽量减少闭包的使用(内存优化)
- 在JS中避免“嵌套循环”和“死循环”
- 尽可能使用事件委托
- 尽量减少CSS表达式的使用(expression)
- CSS选择器解析规则是从右向左解析(基于less/sass开发的时候尽可能减少层级嵌套,目的是让选择器的前缀短一点) 【 a{} 和 .box a{}】
- 尽可能实现JS的封装(低耦合高内聚),减少页面中的冗余代码
- 在CSS导入的时候尽量减少使用@import导入式
- 使用window.requestAnimationFrame(JS中的帧动画)代替传统的定时器动画(能用CSS3动画的绝对不用JS动画)
- 减少递归的使用,避免死递归,避免由于递归导致的栈内存嵌套
- 基于SCRIPT调取JS的时候,可已使用 defer或者async 来异步加载 ……