长文。css
此文主要讲的事情是如何让用户快点看到首屏页面,其主要影响因素是延迟和解析渲染耗时。有关安所有分其实也是优化的一部分。咱们着重说下网络部分。html
大体过程:DNS域名解析、创建TCP链接、下载资源、解析页面。文章描述的优化会尽可能限制在当时的分析的过程下。前端
参考
1.DNS域名解析
通常来说,咱们输入的url是域名,而为了识别一个实体,TCP/IP使用IP地址来惟一肯定一台主机到因特网的链接,DNS会帮助咱们完成域名到IP地址映射的工做。以www.aaa.com
为例,解析过程大体以下:java
过程
-
浏览器ajax
-
本机层segmentfault
- 浏览器客户端向系统询问服务器IP地址,调用本机内的DNS解析程序,检查本身本地的hosts文件是否有这个域名映射关系,没有。
- 查找本机的DNS解析器缓存,没有。
-
路由器缓存浏览器
-
本地DNS服务器缓存
- 本机的DNS解析程序向本地的DNS服务器发起请求,通常为TCP/IP参数中设置的首选DNS服务器,是知道IP地址的,通常会UDP协议。
- 本地DNS服务器查询是否在本地区域文件中,没有。
- 本地DNS服务器查询DNS缓存中是否存在,没有。
- 本地DNS服务器会根据是否设置转发器判断是向上一级DNS服务器(其解析规则同理)仍是直接向根DNS服务器(知道根DNS服务器的IP地址)发送请求。
-
与DNS服务器安全
- 收到请求后,根DNS服务器并不直接解析地址,可是知道每一个顶级域中的一台服务器的地址(如
com
域名服务器)。若是为迭代查询方式,此顶级域DNS服务器的ip被返回给本地DNS服务器。
- 本地DNS服务器提取到顶级域DNS服务器信息后,会再向其发出请求。顶级域DNS服务器收到请求后,会先查询本身的缓存,没有,则将负责的二级域名服务器(如
aaa.com
域名服务器)返回给本地DNS服务器,以此类推直到查到目标域名的映射信息或查询失败。
- 查到映射信息后返回到本机,中间各层会进行缓存。
-
查询方式:性能优化
- 递归方式:一路查下去中间不返回,获得最终结果才返回信息。
- 迭代方式:就是上面的本地DNS服务器与其余域名服务器直接的查询方式,查到一个可能知道的服务器地址,将此地址返回,从新发送解析请求。
- 通常默认的方式从本机到本地DNS服务器是递归,DNS服务器之间是迭代查询。
优化
固然针对DNS的优化就是减小DNS解析的时间,因为浏览器缓存机制的存在,咱们只须要对首次访问进行优化(虽然咱们如今只是请求了一个html文件,可是html文件里还会有咱们后续要请求的css/js/img等),即适当减小要解析的域名个数,考虑到其余优化机制能够将页面及页面内资源发布到2-4个域名上。
2.创建链接
TCP链接
好了,浏览器终于拿到服务器IP了,客户端想要与服务器间通讯并传递消息须要开启TCP(一种传输层协议)链接。
过程
- 客户端建立socket,向服务器目标端口发送链接创建请求,数据段包含位码SYN(创建联机标志位) = 1,随机数seq(顺序号码)= x,和其余TCP标志和选项。
- 服务器有一个专门处理链接请求的welcome socket,接收到链接创建请求,置位码SYN和ACK(确认标志位)为1,ack(确认号码)= x + 1,随机数seq = y,并返回。
- 客户端检查ack是否等于x + 1,等于时,将ACK置为1,SYN置为0,将ack置为y + 1发送至服务器端。
- welcome socket检查ack等于y + 1和ACK等于1后,建立新的socket,此socket由源IP/源端口、目标IP/目标端口标识,以后客户端发送的数据都被引导向此新的socket,至此,TCP链接创建。
简单来说:
// client:
send({SYN: 1, seq: x, ...others})
|
↓
//server:
send({SYN: 1, ACK: 1, ack: x + 1, seq: y, ...others})
|
↓
//client:
ack === x + 1 ? send({ACK: 1, SYN: 0, ack: y + 1, ...others}) : 'hehe'
|
↓
//server:
ack === y + 1 && ACK === 1 ? new Socket() : ''
SSL/TLS
若是启用了HTTPS进行加密,在使用TLS前还须要协商创建加密信道。
过程
- 客户端:TCP链接创建以后,再以纯文本形式发送一些规格说明,随机数
Random1
,TLS协议版本,支持的加密套件列表,支持或但愿使用的其余TLS选项。
-
服务器:
- 取得TLS协议版本以备未来通讯使用,从客户端提供的加密套件列表中选择一个,生成随机数
Random2
发送给客户端;
- 附上本身的证书,将响应发送给客户端;
- 同时,也可发送一个请求,要求客户端提供证书以及其余TLS扩展参数。
-
客户端:
- 同上,可能会向服务器发送本身的证书。
- 客户端收到服务器的证书后,经过证书链关系从根CA(证书的签发机构)验证证书的合法性,验证经过后取出证书中的服务器公钥,生成随机数Random3,再用服务器公钥加密
Random3
(pre master key),发送给服务器;
- 告诉服务器能够开始加密透明信了;
- 客户端用
三个随机数
和约定的加密方法
生成对话密钥
。将前面的握手信息生成完成摘要,使用对话密钥
加密,发送告诉服务器我已完成握手。
- 除了服务器公钥加密的新对称密钥外,全部的数据都是明文形式发送。
-
服务器:
- 用私钥解密出客户端发来的随机数,经过验证消息的MAC检测消息完整性,用相同的方式生成
对话密钥
。
- 解密客户端发送的完成报文,验证
对话密钥
是否正确。
- 告诉客户端,要开始加密了;
- 一样再返回给客户端一个加密的完成消息。
- 客户端用它以前生成的
对话密钥
解密这条消息,肯定对话密钥是否正确
,正确则创建信道而且开始发送应用数据。
其中:
-
对话密钥
又可称为协商密钥
。
-
对话密钥
是对称密钥,对称加解密速度很快。
- 服务器公钥和私钥是非对称密钥,非对称加解密速度很慢。
- 使用非对称加密生成可靠的对称密钥,使用对称密钥进行后续数据的加密。
- 上述带序号报文可能一次发送,也可能分次连续发送。
- SSL和TLS能够看成一个东西。
- 服务器也能够不使用CA颁发的证书,而使用本身的证书。
优化
咱们要对TCP和SSL/TLS握手耗时进行优化。有如下几个因素:
3.得到页面响应
重定向响应
若是服务器返回了跳转重定向(非缓存重定向),那么浏览器端就会向新的URL地址从新走一遍DNS解析和创建链接。
因此应该避免没必要要的重定向。
页面资源响应
在得到了html响应以后,浏览器开始解析页面,进入准备渲染的阶段。下载优化一样放在后面谈到大量请求的时候再说这一点。
4.解析渲染页面
咱们须要将这个过程先分为两个部分来看,页面资源加载和渲染。
页面资源加载
浏览器在解析页面的过程当中会去请求页面中诸如js、css、img等外联资源。
创建链接
一样这些资源的加载也是须要创建TCP链接的,直接使用也须要进行DNS解析和握手。
优化
此处的请求次数与频率相对于第一次请求页面资源时要高不少,因此这里着重阐述下成批量的请求的优化。
浏览器目前使用的HTTP协议版本大可能是1.1和2,两者有些不一样,可是底层都是使用TCP进行数据传输。因为TCP握手耗时,和SSL/TLS更加耗时,咱们须要减小整个加载过程当中须要创建的链接的次数和耗时。
- 复用:针对HTTP1.1的最好方法是启用长链接:HTTP 1.1提供了默认开启长链接功能,相对于短链接(每请求一个资源创建而后断开一次TCP链接),同一客户端socket(浏览器可能会开多个端口并行请求)针对同一socket(域名+端口)后续请求都会复用一个TCP链接进行传输,直到关闭这个TCP链接。
- 加速:针对SSL/TLS握手有会话恢复机制,验证经过后,能够直接使用以前的对话密钥,减小握手往返。
加载以前
在服务器返回响应时,又存在几种状况,如:服务器负载大,服务器宕机,没法及时或较快响应请求,服务器地理位置过远或跨运营商致使延迟很高。
优化
这里跟创建链接部分的优化实际上是公用的,可是单纯的正常创建链接消耗资源较少,因此咱们在这个再较完成的阐述一下。
- 增长带宽:可是大部分状况下服务器带宽并非影响延迟的主要因素。
- 智能DNS解析:根据客户端的IP地址,将域名解析为最近的或不跨运营商的服务器的IP地址,解决地理位置和跨运营商的延迟问题。
- CDN:使用某种分析方式根据节点服务器的地理位置、负载状况、资源匹配状况从遍及各地的节点服务器中找出最合适的静态资源服务器。
- 负载均衡:使用DNS负载均衡、IP负载均衡、反向代理负载均衡等方式从一堆服务器(集群相同职责)或一组服务器(分布式职责区分)中选择最合适的服务器处理请求。
- 这几种技术多是相互结合的,好比CDN会用到DNS智能解析和负载均衡等。
- 其中使用了跳转重定向方式的会从新进行DNS解析和握手,其中一部分优化实际是在域名的DNS解析部分完成的。
开始加载
好了,服务器终于能够愉快的返回数据了。
HTTP 1.1
过程
- 虽然HTTP 1.1有长链接,一个TCP链接能够用来请求多个资源,可是这些资源的下载是串行的,好比使用这个TCP通道请求1.css、2.css、1.js,只有在前者传输成功完整完成后才会进行下一个的传输。
- 虽然浏览器通常会请求创建多个TCP链接(多个端口向服务器一个端口请求资源,新的TCP链接的创建会进行新的握手),去并行的请求页面资源加快总体的下载速度,然而对每一个域名的并行链接是有数量限制的(保护服务器负载,并受主机端口限制),因此咱们仍是要进行一些优化。
优化
-
减小
- 减小页面中须要发起的请求总数,如咱们常规使用的代码合并,雪碧图(精灵图/Sprite合并小图标),将图片转为base64写入其余文件,避免空的img src属性等。
- 切割拆分数据,让首屏数据优先加载等。
- 增长:增长资源的分布域名,部署在不一样域名下,“突破”浏览器并行链接限制(结合DNS部分,不易过于分散,且过多链接会共享带宽,且移动端的解析更加缓慢)。
HTTP 2
因为HTTP 2提供了多路复用的功能,基于二进制数据帧和流的传输,使经过一个TCP链接进行数据分散、乱序、并行传输成为现实,即咱们全部的资源均可以经过一个TCP链接不阻塞的并行传输。
因此咱们针对HTTP 1.1的减小请求数量所作的合并优化、增长资源分布域名都成为了无效优化,能够丢掉。同时因为文件不用合并,进行文件更新时咱们也不用再修改单个开发模块更新全部(合并文件)模块了。
加载中
总的来讲是很简单的过程,客户端接收服务器传输返回的响应。
优化
传输的数据大小越小,那么传输就越快,延迟就越小。
关闭TCP
在资源下载完毕以后,须要关闭TCP链接。这段没有什么能够优化的。
过程:
- TCP客户端发送一个FIN = 1(结束标志位)和随机数seq = a,用来关闭客户到服务器的数据传送。
- 服务器收到这个关闭请求,返回ACK = 1 ,ack = a + 1,此时服务器以前的数据可能尚未传输完成。
- 数据传输完成后,服务器发送一个FIN = 1和随机数seq = b给客户端。
- 客户端返回ACK = 1,ack = b + 1,并等待一段时间,确保服务器没有返回没收到确认报文的重传申请,后关闭链接。
- 服务器收到确认报文后,验证关闭链接。
总结
HTTP2 真好用。合理使用缓存。
页面解析渲染
上述的资源加载是发生在页面解析过程当中的。那么浏览器的页面解析渲染是怎么样的一个过程呢?
过程
简要来说就是:
- 处理HTML标记,构建DOM树。
- 处理CSS标记,构建CSSOM树。
- 将DOM树和CSSOM树融合成渲染树(会忽略不须要渲染的dom)。
- 根据渲染树来布局,计算每一个节点的几何信息。
- 在屏幕上绘制各个节点。
- 中间遇到各类资源时,会进行资源的下载。
问题
-
资源下载
- css下载时会阻塞渲染(带有media属性除外)。
- 遇到 script 标签时,DOM构建中止,此时控制权移交至js,直到脚本(下载)执行完毕,此时浏览器有优化通常会下载其余资源,可是不会解析。若是js中有对CSSOM的操做,还会先确保CSSOM已经被下载并构建。
- 图片资源下载不会产生阻塞。
-
重绘重排致使从新进行渲染树的生成
- 重排(回流):会从新计算布局,一般由元素的结构、增删、位置、尺寸变化引发,如:img下载成功后,替换填充页面img元素,引发尺寸变化;也会由js的属性值读取引发,如读取offset、scroll、cilent、getComputedStyle等信息。
- 重绘:简单外观的改变会引发重绘,如颜色变化等。
- 重排必定重绘。
优化
-
dom
- 简化dom结构,减小DOM树和渲染树构建成本,减小页面元素个数,如使用列表表格数据分页,简单表格不要使用复杂第三方组件等方式。
-
js
- 将js脚本标签放在页面body底部,减小对其余过程的阻塞。
- 延迟执行:对不修改页面的外链script使用defer属性,使脚本并行下载不阻塞,下载后不马上执行,而在全部元素解析以后执行。
- 减小和合并没必要要的dom相关操做,如使用DocumentFragment、修改classname而不是各项style等,减小对重绘和重排的触发。
-
css
- 将css放入head中,提早加载,并防止html渲染后从新结合css引发页面闪烁。
- 减小css层级和css选择器复杂度,提升解析速度,虽然浏览器有优化。
- 使用更高性能的css样式,如flex代替float。
- 开启复合层,如使用3d变换、opacity等,使该元素及其子元素不致使外部的重排,可是也有坑。
- 合理使用脱离文档流的样式,减小对外部重排的影响,如absolute。
-
文件数量
- 减小首次下载的文件数量大小,使用图片懒加载,js的按需加载等方式,也能够节省用户流量,甚至使用storage存储进行js、css文件的缓存。
- 拆分页面资源,首屏数据优先加载等。
5.其余优化措施
咱们还能够采起一些和延迟、渲染无关的优化措施:
- 使用PWA,让用户在没有获得数据时也能看到页面。
- 对页面某些ajax请求数据进行storage存储。
- 加载进度、骨架图、占位图等相似让用户感受好一点的措施。
- 及时更新升级服务器,优化措施依赖于服务器支持。