从浏览器原理分析界面性能优化01
从浏览器原理分析界面性能优化—浏览器的网络请求
众所周知,当我们在浏览器的地址栏输入一个域名并且回车后,浏览器就能够进行网络请求,然后将我们想要的界面展示出来.
但是这个过程浏览器到底经历了什么,我们又可以怎样进行优化,这是我们今天要讨论的问题
浏览器运行过程
我们都知道,在多进程浏览器架构的模式下,从输入 URL 到发起网络请求是需要多个进程的配合的.
我们先看一下浏览器进程和网络进程的作用:
- 浏览器进程:负责进程调度,用户交互,数据存储等功能
- 网络进程:提供网络服务下载功能
为了简单明了,先附一张简单的流程图
用户输入 URL
当我们输入一段信息之后,浏览器会先判断我们输入的是一段搜索信息还是一个URL.
- 如果是搜索信息,浏览器会使用默认的搜索引擎,合成带由搜索关键字的 URL
- 如果是 URL 浏览器会添加上网络协议,将其合成为一个完整的 URL
用户点击回车
当我们输入 URL,浏览器处理完毕,这个时候我们点击回车意味着我们同意将当前界面替换成新的界面.浏览器开始继续工作
浏览器在离开当前页面之前回调用 beforeunload 方法,我们来看一下 MDN 上对该方法的描述
事件使网页能够触发一个确认对话框,询问用户是否真的要离开该页面。如果用户确认,浏览器将导航到新页面,否则导航将会取消
根据规范,要显示确认对话框,在事件处理上需要调用 preventDefault()
该事件的主要作用就是提示用户是否离开当前界面,当我们确认离开当前界面以后,浏览器就开始进行网络请求了
网络进程的处理
当处理完 beforeunload 之后,浏览器会将处理过后的 URL 通过进程间通信(IPC)发送给网络进程,网络进程收到 URL 以后会发起真正的网络请求过程.
主要分以下几步:
查找本地缓存
网络进程会先查找本地是否缓存了资源,如果有缓存资源,那么该请求返回200,并把相应资源返回给浏览器进程,
PS: 有一点需要注意的是,这里的缓存指的是强缓存
如果没有命中强缓存的话,则会进行下一步的 DNS 解析
DNS 解析
DNS 解析的目的是通过域名去获取服务器的 IP 地址和对应的端口号,如果没有端口号,那么 http 协议默认是 80 端口,https 协议默认是 443 端口,如果请求是 HTTPS 协议,还需要建立 TSL 连接
PS:DNS 解析也是会先检查之前有没有缓存过该域名的 IP 地址的,如果能够命中缓存则直接返回缓存内容,否则去请求本地域名服务器进行后续的操作
DNS 解析完成后我们就获得了该 URL 的 IP 地址,下面就可以进行真正的网络请求了
与服务器建立连接传输数据
这个过程是通过 IP 地址与服务器建立连接
需要注意的是,在 Chrome 浏览器中,同一域名下最多建立 6 个 TCP 连接,超过这个数量,后续的连接将会进入等待状态,等待前面的 TCP 连接释放后才能够建立新的连接.
我们现将网络大致看作应用层、传输层、网络层,那么数据的传输可以大致分为下面几步
- TCP 三次握手建立连接
- 应用层中的 HTTP 协议生成针对目标的 Web 服务器的 HTTP 请求报文(同时也负责对接收到的网络内容做处理)
- 传输层中的 TCP 协议将 HTTP 请求报文按序号分割成报文段,添加上目标程序端口号和源端口号(同时,在接收请求过程中他也负责将报文段按照顺序拼接成一个完成的报文)
- 网络层中的 IP 在数据包中添加上 IP 头部(包括目标 IP 和源 IP)
- 当请求通过物理层的中转传输到达对方服务器后,服务器通过逆向的上述流程解析请求数据
- 如果需要重定向,服务器返回相应的状态码(301、302、303 等),同时在 Location 字段附上相应的重定向地址,浏览器会根据状态码和地址进行重定向操作
- 如果不是重定向,服务器会检查是否命中协商缓存(协商缓存中的 Etag 优先级要高于 LostModify,但是两者各有优劣,是互补关系),如果命中协商缓存,则服务器返回 304 和空的请求体
- 如果没有命中协商缓存,则服务器处理请求,返回 200 和相应的资源
- 客户端按照网络层=>传输层=>应用层的顺序开始接收数据
网络进程的处理
到了接收数据的这一步,网络进程会将获取的数据包进行解析,然后通过响应头中的 Content-type 字段判断数据类型,如果是字节流的类型,则将接收到的数据交给下载管理器,该导航结束,不再进行
如果是 text/html 等的类型,就通过浏览器进程获取到文档准备开始下一步的渲染工作
以上,就是从我们输入 URL 到界面显示过程中的网络请求部分,在这个部分中我们可以看到,占比重最大的就是网路进程中的网络请求部分了,那么我们下面来看一下我们可以从什么地方就行优化
优化的思路
HTTP 优化的方向由两个(关于缓存问题,放到后面浏览器缓存部分):
- 减少请求次数
- 减少单次请求所花费的时间
其实,上面两个方向已经包含在关键渲染路径(CRP)里面了,这也是我们讲到性能优化的时候绕怎么也不开的一个话题.
关键渲染路径主要包括三部分:
- 关键资源数量
- 关键路径长度
- 关键字节数
上面的关键资源数量,其实就可以看作是减少网络请求次数,关键字节数和关键路径长度即可以简单的归类为减少了单次请求花费的时间.
减少请求次数
在 HTTP1.1 的环境下,我们如果想减少请求的次数,大概可以有以下选择:
- 适当的使用内联的 CSS 和 JS,避免过多的脚本文件的请求
- 使用 async/defer 或者 preload/prefetch 对文件进行异步加载(这个在后面的浏览器渲染流程文章中会有讨论)
- 对于小型图片的话可以使用 Sprite 图,进行图片的合并.(在 HTTP2.0 的情况下需要另行讨论)
减少单次请求所花费的时间
压缩文件体积和管线化
单次请求花费的时间,我们可以从两方面着手,一方面是压缩文件的体积,另一方面是使用持久链接或者管线化的方式进行通信.
PS:在 HTTP1.1 中是默认开启持久链接的,但是在 HTTP1.1 之前的版本默认链接都是非持久链接.所以,如果你想在旧版本的 HTTP 协议上使用持久链接,需要指定请求头的 Connection 为 Keep-Alive
使用 http 压缩
减少单次请求的时间也可以使用GZip
gzip 是由文件压缩程序 gzip(GUN zip)生成的编码格式采用 Lempel-Ziv 算法及 32 位循环冗余校验
deflate:组合使用 zlib 格式及由 deflate 雅座算法生成的编码格式
我们可以通过请求中的Accept-Encoding:,来告诉服务器用户代理支持的内容编码以及内容编码优先级顺序,例如Accept-Encoding:gzip,deflate,就是告诉服务器,浏览器支持 gzip 和 delate 两种格式的压缩,这个时候服务器可以传输给我们 gzip 或者 deflate 压缩格式的数据了
使用 gzip 可以压缩我们的数据节省好多流量,也是一种减少单次请求时间的方式
压缩文件的体积
我们可以使用 Webpack 对我们的文件进行压缩和摇树的操作,去掉空白行、回车符、默认注释等.
同时,我们网站中图片的选择也是一个重中之重的问题,不同格式的图片的大小和质量是不同的.这也是一个质量和加载速度之间的权衡.我们后面的文章中会有提到,不同的图片对加载速度和质量的影响.这里先占个坑.