# 页面加载全过程的优化策略分析
# 客户端发起请求阶段
客户端请求阶段的过程主要是用户输入 URL 之后,浏览器会查找本地是否已经缓存了这个网站。如果没有,就会进行 DNS 查询获取 IP 地址,接下来进行 TCP 的三次握手和 TLS 协商向服务器发起 HTTP 请求建立连接。
# 本地缓存
本地缓存可以让静态资源加载更快,当客户端发起请求时,静态资源可以直接向客户端中获取,不需要再向服务器请求。
前端本地缓存的两种方式:
- 强缓存:
- 浏览器在加载资源的时候,根据请求头中的 expires 和 cache-control 判断是否命中客户端缓存
- 协商缓存
- 协商缓存会先发送一个请求到服务器,服务器根据 Last-Modified 和 etag 判断是否命中缓存
# DNS 查询
每进行一次 DNS 查询,都会经过用户设备到移动信号塔,再到 DNS 认证服务器的过程。很耗费时间。
优化方式:
尽量减少 DNS 请求的次数
让 DNS 查询走缓存,而不是在跳转时再进行 DNS 的查询,使用 DNS预解析(dns-prefetch)
dns-prefetch :
- 前端优化系列:DNS预获取 dns-prefetch 提升页面载入速度_dns prefecth-CSDN博客 (opens new window)
- DNS 预解析是什么?怎么实现?_dns预解析-CSDN博客 (opens new window)
# HTTP 请求
HTTP 请求的瓶颈在于请求阻塞,因为浏览器为了保证访问速度,默认对同一域名的请求是有最大连接数的限制的,一般最多只能并发6个,请求过多就会导致阻塞。
一些主流浏览器对 HTTP 1.1 和 HTTP 1.0 的最大并发连接数目
| Browser | HTTP/1.1 | HTTP/1.0 |
|---|---|---|
| IE 11 | 6 | 6 |
| IE 10 | 6 | 6 |
| IE 9 | 10 | 10 |
| IE 8 | 6 | 6 |
| IE 6,7 | 2 | 4 |
| Firefox | 6 | 6 |
| Safari 3,4 | 4 | 4 |
| Chrome 4+ | 6 | 6 |
| Opera 10.51+ | 8 | ? |
优化方式:
- 域名规划: 尤其是对于首屏来说,看看页面中需要用到哪些域名,对这些域名的请求顺序进行规划
- 域名散列:
将静态资源请求的域名散列到不同的域名上,这样可以增加并行请求的连接数。比如我们想要同时向
www.google.com发起10个请求,获取相应的静态资源。而当前浏览器限制同一域名下并发的最大连接数是6。此时我们就要进行域名的散列,将原先的www.google.com散列为www.google0.com、www.google1.com、www.google2.com或者更多,每次请求时随机选取一个域名进行请求,这样最多就可以同时并发18个HTTP连接了。
# 服务端数据处理阶段
服务端数据处理阶段是指服务器收到请求之后,从数据存储层取到数据,再返回给前端的过程。每请求一次数据,都需要从客户端到服务器,在到更后端的数据存储层,然后再一层一层的返回到前端,耗时很长。
# Gzip
Gzip 是一种压缩技术,服务端通过 Gzip 压缩,传输到客户端的资源的大小可以变为原来的三分之一左右,可以极大程度的优化资源的下载时间,大大的提高页面的展示速度。一般来说,运维团队会默认开启 Gzip 压缩,不需要前端处理。
# 数据缓存
# 借助 Service Worker 进行数据接口缓存
service worker 是浏览器的一个高级属性,本质上是一个请求代理层,它存在的目的就是拦截和处理网络数据请求
# 借助本地存储的接口缓存
对于一些数据时效性要求不高的数据,在第一次请求到数据之后,将数据存储在本地存储当中,下一次请求的时候,先去缓存中获取资源,如果没有的话,再向服务器发起请求。
# CDN
Content Delivery Network,中文叫内容分发网络。通过在现有的Internet中增加一层新的CACHE(缓存)层,将将用户的请求导向离用户最近的服务节点上,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。
# 重定向
重定向是指网站的资源迁移到其他位置之后,用户访问站点时,程序自动将用户的请求从一个页面转移到另一个页面的过程。这个过程中会引发新的 DNS 查询、TCP 三次握手以及 TLS 协商,并且产生新的 HTTP 请求。非常耗费时间。
# 页面解析和渲染阶段
解析:HTML 解析器把页面内容转换为 DOM 树和 CSSOM 树的过程
DOM 树:全称 Document Object Model ,即文档对象模型,描述了各个标签之间的层次结构
CSSOM 树:即 CSS 对象模型,主要描述样式集的层次和结构。CSS 解析器遍历每个 CSS 样式规则,将 CSS 规则解析成浏览器可以解析和处理的样式集合,最终结合浏览器中的默认样式,汇总形成具有父子关系的 CSSOM 树。
渲染:主线程会计算 DOM 节点的最终样式,生成布局树,布局树会记录参与页面布局的节点和样式
绘制:把各个节点绘制到屏幕上的过程。结果以层的方式保存。当文档中各个部分以不同的层绘制时,相互重叠时,就必须进行合成。
# 构建 DOM 树的优化
- 规范 HTML 语法。当 HTML 语法有小的错误时(比如
<br>标签少写了结束符号之类的),浏览器会进行语法纠错,这样就导致页面的解析时间变长,影响页面性能。 - 尽量精简 DOM 的数量。因为 DOM 节点数量越多,构建 DOM 树的时间就越长,进而导致页面的解析时间变长,影响页面性能。
- 延迟加载或使用 defer 和 async 优化 script 标签的资源请求。无论是 DOM 还是 CSSOM ,都可以被 JS 所修改,所以,一旦页面在解析的时候遇到了
<script>标签,DOM 的解析构建就会被暂停,等待服务器下载并执行脚本资源之后,才会继续解析 DOM。因此,外部<script>标签经常被称为解析阶段的拦路虎,有时就因为解析过程中多了一个外部script标签,页面的解析阶段就由 200ms 变成了1s。所以,外部脚本的加载时机非常重要,能够延迟加载就延迟加载,或者通过使用 defer 和 async 告诉浏览器,在等待脚本下载的期间,不暂停 HTML 解析。
# 布局阶段的优化
布局阶段,浏览器根据CSS解析器给出的样式规则,来计算某个元素需要占据的空间的大小和在页面中的位置,借助计算的结果来进行布局。
尽量不要在渲染阶段改变元素样式:主线程采用的是流模型的布局方式,即从上到下、从左到右进行遍历所有的元素进行布局。如果我们在渲染阶段改变了一个元素的布局属性,那么此时浏览器必须检查所有其他的元素,然后自动重排页面,受到影响的元素需要重新绘制,相当于重新进行了一次渲染过程。
尽量精简 DOM 数量:因为浏览器每次布局都需要作用于整个 DOM 对象,如果节点数量多,计算所有的元素位置和尺寸会耗费很长时间。
上述第一点很重要,我在做博客列表页项目时,一开始布局的时候,没有给定列表封面图片的初始大小,这样会导致只有当图片加载完成之后,主线程才知道了图片的大小,不得不重新进行布局计算,然后再次进行页面渲染(重绘 + 重排),导致页面加载时间过长。所以后面采取了给定封面图片大小的方式,避免了这些浏览器的重复布局。