作者:前端学苑 公号 / 前端小贾 (本文来自作者投稿)
编者荐语
性能是前端编码规范、网络层面、辅助工具等相互作用的结果。
这是一个兼顾广度和深度的问题,优化好了可以加快首屏加载速度提高用户留存率,节省服务器资源降低成本等,也是区分初高级前端工程师的重要标准。
本文重点
- 掌握HTTP报文及其在实践中的运用
- 掌握浏览器中的“强缓存” 和 “协商缓存”
- 掌握TCP三次握手和四次挥手的运行机制及意义
- 掌握HTTP1.0/1.1/2.0之间的区别
为了把HTML、CSS和JavaScript转化成活灵活现、绚丽多彩的网页,浏览器需要处理一系列的中间过程。
优化性能
其实就是了解这个过程中发生了什么
即CRP(Critical Rendering Path,关键渲染路径)。
从输入 URL 到页面展示,这中间发生了什么?
1)URL解析
2)缓存检查
3)DNS 解析:将域名解析成 IP 地址;
4)TCP 连接:TCP 三次握手;
5)发送 HTTP 请求;
6)服务器处理请求并返回 HTTP 的报文;
7)浏览器解析渲染页面;
8)断开连接:TCP 四次挥手;
整个过程的关键点展开的说一下。
URL解析
1、地址解析
HTTP 是明文传输协议,连接简单,是无状态的。
HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
端口号:HTTP 80,HTTPS 443。FTP 21
2、编码(面试常问)
encodeURI 编码/ decodeURI 解码
对整个URL的编码:处理空格/中文...
var uriStr = "http://www.baidu.com?name=张三&num=001 zs";
var uriec = encodeURI(uriStr);
document.write("编码后的" + uriec);
编码后的http://www.baidu.com?name=%E5%BC%A0%E4%B8%89&num=001%20zs
var uridc = decodeURI(uriec);
document.write("解码后的" + uridc);
解码后的http://www.baidu.com?name=张三&num=001 zs
encodeURIComponent / decodeURIComponent
主要对传递的参数信息编码
let url = `http://www.jb51.net/api/?lx=1&
name=${encodeURIComponent("前端学苑")}&from=${encodeURIComponent("http://www.baidu.com/")}`
结果:
http://www.jb51.net/api/?lx=1&
name=%E5%89%8D%E7%AB%AF%E5%AD%A6%E8%8B%91&from=http%3A%2F%2Fwww.baidu.com%2F
URI / URL / URN
(1)URI
统一资源标识符,用来唯一的标识一个资源。
(2)URL
统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,
而且还指明了如何locate这个资源。
(3)URN
统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。
缓存检查
缓存检测:产品性能优化的重要手段。
缓存位置:
1)Memory Cache : 内存缓存
2)Disk Cache:硬盘缓存
补充回答:(面试常问)
1)打开网页:查找 disk cache 中是否有匹配,如有则使用,如没有则发送网络请求。
2)普通刷新:因TAB没关闭,因此memory cache是可用的,会被优先使用,其次才是disk cache。
3)强制刷新:浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache
,服务器直接返回 200 和最新内容。
说明:页面刷新的情况:先看有没有内存,如果没有走硬盘。页面重新加载:直接走硬盘。
注: html 页面一般不做强缓存:每一次html的请求都是正常的HTTP请求。
缓存方式
- 强缓存(Expires / Cache-Control)
- 协商缓存(Last-Modified / ETag)
强缓存
强缓存 Expires / Cache-Control浏览器对于强缓存的处理:根据第一次请求资源时返回的响应头来确定的。
1)Expires: 缓存过期时间,用来指定资源到期的时间(HTTP/1.0
)
2)Cache-Control: cache-control: max-age=2592000第一次拿到资源后的2592000秒内(30天),再次发送请求,读取缓存中的信息(HTTP/1.1
)。
3)两者同时存在的话,Cache-Control优先级高于Expires。
补充回答:(面试常问)
强缓存问题,如果服务器文件更新了,但是本地还是有缓存,这样不就拿不到最新的信息了吗?
处理方式:
1)服务器更新资源后,让资源名称和之前不一样
,这样页面导入全新的资源。
例如:
index.abc.js
index.cde.js
处理结果:webpack hash name。
2)当文件更新后,我们在html导入的时候,设置一个后缀[时间戳]
。
例如:
<script src="index.js?123456"><script src="index.js?234234">
处理结果:随机码或时间戳。
协商缓存
协商缓存与强缓存区别:协商缓存总会和服务器协商,所以一定要发HTTP请求的。
协商缓存 Last-Modified / ETag协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求
,由服务器根据缓存标识决定是否使用缓存的过程。
补充回答:
第一次向服务器发送请求
协商缓存缓存没有,向服务器发送请求 (没有传递任何的标识);
服务器收到请求准备内容;
last-modified
: 资源文件最后更新的时间;
ETag
:记录的是一个标识 (也是根据资源文件更新生成的,每一次资源更新都会重新生成一个 ETag);
说明:客户端拿到信息后渲染,把信息和标识缓存到本地。
第二次发请求
if-Modified-Since = Last-Modified 值
;if-None-Match = ETag 值
;
给服务器;
说明:服务器根据标识判断文件是否更新。
扩展问题:
1)304时,本地缓存文件丢失了怎么办?会向服务器再发请求 ,获取最新的数据。
2)什么情况下强缓存协商缓存都要设置?一般情况下都需要设置的。
3)强缓存与协商缓存策略,针对于我们的静态资源文件,而且是不经常更新的。
数据缓存
cookie 和 session 区别 (面试常问)
1)cookie
数据存放在客户的浏览器上,session
数据放在服务器上。
2)cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie欺骗,考虑到安全应当使用session
。
3)session
会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
。
4)单个cookie
保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
。
5)可以考虑将登陆信息等重要信息存放为session
,其他信息如果需要保留,可以放在cookie
中。
DNS解析
在地址栏输入的域名并不是最后资源所在的真实位置,域名只是与IP地址的一个映射。
DNS域名解析有两种方法:递归查询
和迭代查询
首先走递归查询:主机向本地域名服务器的查询采用递归查询。
其次走迭代查询:本地域名服务器向根域名服务器的查询
采用迭代查询。
每一次DNS解析时间预计在20\~120毫秒
1)减少DNS请求次数
2)DNS预获取(DNS Prefetch)
<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="//d.3.cn"/>
<link rel="dns-prefetch" href="//d.jd.com"/>
服务器拆分的优势
1)资源的合理利用;
2)抗压能力加强;
3)提高HTTP并发;
TCP三次握手
TCP三次握手,建立连接通道
1) 客户端发送一个 SYN (标志)
码给服务器,要求建立数据连接。(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
2) 服务器SYN和自己处理一个SYN (标志);叫SYN+ACK (确认包)
;发送给客户端,可以建立连接。(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
3) 客户端再次发送ACK向服务器
,服务器验证 ACK 没问题,建立连接。(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)
seq序号,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记;
ack确认序号,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1;
标志位
ACK:确认序号有效
RST:重置连接
SYN:发起一个新连接
FIN:释放一个连接
三次握手为什么不用两次,或者四次? (面试常问)
TCP
作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率!
数据传输
HTTP请求报文与响应报文格式
请求报文包含:
- 请求行:包含请求方法、URI、HTTP版本信息;
- 请求头;
- 请求体。
响应报文包含:
- 响应行:包含HTTP版本、状态码、状态码的原因短语;
- 响应头
- 响应体
HTTP Request Header 常见的请求头:
1)Accept: 浏览器能够处理的内容类型 application/json, text/javascript, /; q=0.01
2)Accept-Charset: 浏览器能够显示的字符集
3)Accept-Encoding: 浏览器能够处理的压缩编码 gzip application/json, text/javascript, /; q=0.01
4)Accept-Language: 浏览器当前设置的语言 zh-CN,zh;q=0.9,en;q=0.8
5)Connection: 浏览器与服务器之间连接的类型 keep-alive
6)Cookie: 当前页面设置的任何 Cookie
7)Host: 发出请求的页面所在的域 www.baidu.com
8)Referer: 发出请求的页面的URL
9)User-Agent: 浏览器的用户代理字符串
HTTP Responses Header常见响应头:
1)Date: 表示消息发送的时间,时间的描述格式由rfc822定义
2)server: 服务器名字
3)Connection: 浏览器与服务器之间连接的类型
4)content-type: 表示后面的文档属于什么MIME类型
5)Cache-Control: 控制HTTP缓存
HTTP 状态码 (面试常问)
101: 切换协议
200: 请求被正常处理
206: 客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。(视频大小)
301: 永久性重定向
302: 临时重定向
304: 发送附带条件的请求时,条件不满足时返回,与重定向无关 (用于缓存控制)
307: 协商缓存
400: 请求无效
401: 请求需要认证
403: 服务器已经得到请求,但是拒绝执行
404: 服务器无法找到对应资源
500: 服务器内部错误
502: 网关错误
503: 服务器正忙
TCP四次挥手
1)客户端发送 FIN (结束)报文
,通知服务器数据已经传输完毕;(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧) 用来关闭Client到Server的数据传送。
2)服务器接收到之后,通知客户端我收到了SYN,发送ACK(确认)给客户端
,数据还没有传输完成。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
3)服务器已经传输完毕,再次发送FIN通知客户端
,数据已经传输完毕;(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧) 用来关闭Server到Client的数据传送
4)客户端再次发送ACK,进入TIME\_WAIT状态
,服务器和客户端关闭连接。(第四次挥手:由浏览器发起,告诉服务器,我的响应报文接受完了,我准备关闭了,你也准备吧)
为什么连接的时候是三次握手,关闭的时候却是四次握手?(面试常问)
1)服务器端收到客户端的SYN
连接请求报文后,可以直接发送SYN+ACK
报文。
2)但关闭连接时,当服务器端收到FIN
报文时,很可能并不会立即关闭链接,所以只能先回复一个ACK
报文,告诉客户端:”你发的FIN报文我收到了”,只有等到服务器端所有的报文都发送完了,我才能发送FIN
报文,因此不能一起发送,故需要四步握手。
Connection: keep-alive
当使用Keep-Alive
模式时,Keep-Alive功能使 客户端到服务器端的连接持续有效,对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。
通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率 (更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。
HTTP版本间的对比
HTTP1.0和HTTP1.1的一些区别
1)缓存处理
HTTP1.0
中主要使用 Last-Modified,Expires 来做为缓存判断的标准,HTTP1.1
则引入了更多的缓存控制策略:ETag,Cache-Control…
2)带宽优化及网络连接的使用
HTTP1.1
支持断点续传,即返回码是206(Partial Content)
3)错误通知的管理
在HTTP1.1
中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除…
4)Host头处理
在HTTP1.0
中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
5)长连接
HTTP1.1
中默认开启Connection:keep-alive
,一定程度上弥补了HTTP1.0
每次请求都要创建连接的缺点。
HTTP2.0和HTTP1.X相比的新特性
1)新的二进制格式(Binary Format)
HTTP1.x的解析是基于文本,基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合,基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
2)header压缩
HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
3)服务端推送(server push)
例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了
// 通过在应用生成HTTP响应头信息中设置Link命令
Link: styles.css>; rel=preload; as=style, </example.png>; rel=preload; as=image
4)多路复用(MultiPlexing)
HTTP/1.0
每次请求响应,建立一个TCP连接,用完关闭HTTP/1.1
「长连接」 若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;HTTP/2.0
「多路复用」多个请求可同时在一个连接上并行执行,某个请求任务耗时严重,不会影响到其它连接的正常执行;
感谢大家
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
- 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
- 欢迎加我微信「Augenstern9728」一起交流学习...
- 关注公众号「前端时光屋」,持续为你推送精选好文。