前端性能优化笔记(二):从输入URL到页面加载过程分析

·  阅读 249
前端性能优化笔记(二):从输入URL到页面加载过程分析

写在前面

在页面加载到最终渲染显示大致是这样的:用户在浏览器输入URL回车后,浏览器为了将URL解析成IP地址,会向DNS服务器发起DNS查询,获取IP地址。在建立连接后,浏览器就可以发起HTTP请求,而服务器接受请求后进行响应,浏览器从响应结果中拿到数据,并进行解析和渲染,最后在用户面前就出现了一个网页。简而言之就是三个阶段:

  • 客户端发起请求阶段
  • 服务端数据处理请求阶段
  • 客户端页面渲染阶段

客户端请求阶段的优化点

客户端发起请求阶段是指用户在浏览器输入URL,经过本地缓存确认是否已经存在这个网站。如果没有,接着会由DNS查询从域名服务器获取这个IP地址,接下来就是客户端通过TCP三次握手和TLS协商向服务器发起HTTP请求建立连接的过程。

本地缓存

本地缓存可以让静态资源加载更快,当客户端发起一个请求时,静态资源可以直接从客户端获取,不需要再想服务器请求。

但是在实际开发中,很多前端程序员会忽略本地缓存的优化,这就会导致:在客户端请求阶段,假设一个项目的列表页DNS产生时间是835ms,TCP三次握手和TLS协商是436ms,数据返回是412ms,这样在强网条件下一个请求的响应时间大概是1233ms。如果在弱网条件,一个请求连接的时间都需要2s,但是使用缓存处理的话,几乎可以说是几ms内完成请求。

强缓存:指的是浏览器在加载资源时,根据请求头的expires和cache-control判断是否命中客户端缓存。

协商缓存:指的是浏览器会先发送一个请求到服务器,通过last-modified和etag验证资源是否命中客户端缓存。

DNS查询

DNS之所以能够成为前端性能的优化点,这是因为每进行一次DNS查询,都要经历从客户端到信号接收站,再到认证DNS服务器的过程。

但是这样每次查询都要走这个流程就会耗费很多的时间,优化方法就是让DNS查询先缓存,而浏览器提供了DNS预获取的接口,我们可以在打开浏览器或者Webview的同时就进行配置。

HTTP请求

对于HTTP请求而言最大的优化点在于请求阻塞,就是浏览器为了保证访问速度,会默认对同一域下的资源保持一定的连接数,请求过多会进行阻塞。对此我们提前做好域名规划是很重要的,可以先看看当前页面需要用到哪些域名,最关键的是首屏中需要用到哪些域名。

域名散列:就是通过不同的域名,增加请求并行连接数。将静态服务器地址pic.yichuan.com,做成支持pic0-5的6个域名,每次请求时随机选取一个域名地址进行请求,因为有6个域名同时可用,最多可以进行并行36个连接。

一次完整的HTTP请求需要经历DNS查找,建立TCP握手,浏览器发起HTTP请求,服务器接受请求并处理返回响应结果,浏览器再接收响应等过程。但是每一次HTTP请求都需要加载很多文件,建立连接并耗费很多时间。如果有很多文件就需要发起很多次请求,而如果把若干个小文件合并成一个大文件就可以减少HTTP请求,减少访问的时间、提升效率和速度。

服务端数据处理阶段的优化点

服务端数据处理阶段指的是WevServer接受到请求后,从数据存储层取到数据,再返回给前端的过程。服务端程序接受到HTTP请求后,会做一些请求参数处理以及权限校验。此过程的优化点:在于是否做了数据缓存处理、是否做了gzip压缩以及是否具有重定向。gzip压缩是一种压缩技术,通过gzip压缩资源的下载速度会快很多,能够大大提升页面的展示速度。

数据缓存

在进行数据缓存的几种方法:

  • 借助Service Worker的数据接口缓存
  • 借助本地存储的接口缓存
  • CDN

Service Worker:是浏览器的一个高级属性,本质上是一个请求代理层,它存在的目的就是拦截和处理网络数据请求。

借助本地存储的接口缓存:指的是在一些对数据时效性要求不高的页面,第一次请求到数据后,程序将数据存储到本地存储。下一次请求的时候,先去缓存里面取出数据,如果没有的话再想服务器发起请求。

CDN:基本思路是通过在网络各处放置节点服务器,构造一个智能虚拟网络,将用户的请求导向离用户最近的服务节点上。

为什么数据缓存会成为性能的优化点呢?这是因为每次请求数据接口,需要从客户端到后端服务器再到更后端的数据存储层,一层一层返回数据,最后再返回客户端,这样请求响应的耗时很长。

重定向

重定向是指网站资源迁移到其他位置后,用户访问站点时,程序会自助将用户请求从一个页面转移到另外一个页面的过程。重定向的三种方式:

  • 服务端发挥的302重定向
  • META标签实现的重定向
  • 前端Javascript通过window.location实现的重定向

它们都会引发新的DNS查询,会导致新的TCP三次握手和TLS协商以及产生新的HTTP请求,而这些都会导致请求过程中更多地时间,进而影响前端性能。

当前服务端对数据加工聚合处理后,客户端拿到数据,接下来会进入解析和渲染阶段。解析阶段就是HTML解析器将页面内容转换成DOM树和CSSDOM树的过程。所谓DOM树,就是文档对象模型(Document Object Model),它描述了标签之间的层次和结构。CSSDOM树,即CSS对象模型,主要描述了样式集的层次和结构。

CSS解析器遍历其中每个规则,将CSS规则解析浏览器可解析和处理的样式集合,最终结合浏览器里面的默认样式,汇总形成具有父子关系的CSSDOM树。

页面解析和渲染阶段的优化点

主线程会计算DOM节点的最终样式,生成布局树,布局树会记录参与页面布局的节点和样式。

DOM树解析中的优化点

解析和渲染阶段的流程环节比较多,逻辑复杂,优化点也比较多,比如:DOM树构建过程,CSSDOM树生成阶段,重排和重绘过程等。

  • 当HTML标签不满足web语义化时,浏览器就需要更多时间去解析DOM标签的含义。
  • DOM节点的数量越多,构建DOM树的时间就越长,进而延长解析时间,拖延页面展示速度。
  • 文档中包含<script>标签时,无论是DOM或者是CSSDOM都可以被Javscript所访问和修改,所以一旦在页面解析时遇到<script>标签,DOM的构造过程就会暂停。因此外部<script>标签常被称为”解析“阶段的拦路虎,有时就因为解析过程中多了一个<script>标签造成页面解析阶段从200ms到1s。对此,外部脚本的加载时机一定要明确好,能够延迟加载就选用延迟加载,通过使用defer和async告知浏览器在等待脚本下载期间不阻止解析过程。

CSS执行会阻塞渲染,阻止JS执行,而JS加载和执行会阻塞HTML解析,阻止CSSDOM构建。如果这些CSS、JS标签放在<head>标签中,并且需要加载和解析很久的话,那么页面就出显现白屏情况。因此,JS文件要放在底部(不会阻止DOM解析,但是会阻塞渲染),等HTML解析后再加载JS文件,尽早向用户呈现页面的内容。

之所以要讲CSS文件放在头部,这是因为加载HTML后再加载CSS,会让用户第一时间看到没有样式的页面,为了避免出现这种情况需要将CSS文件放在头部。当然JS文件也可以放在头部,但是需要在<script>标签加上defer属性就可以了,异步进行下载、延迟执行。

布局中的优化点

浏览器会根据样式解析器给出的样式规则,来计算某个元素需要占据的空间大小和屏幕中的位置,借助计算结果来进行布局。而主线程布局是采用的流布局,就是从上到下、从左到右进行遍历进行布局。

假设我们在页面渲染过程运行时修改了一个元素的属性,这时布局阶段受到了影响,浏览器必须检查所有其他区域的元素,然后自动重排页面,相当于进行了一遍整个渲染流程。

此外,因为浏览器每次布局计算都要作用于真个DOM,如果元素量大,计算出所有的元素位置和尺寸会花费很长的时间,所以布局阶段很容易成为性能瓶颈点,需要我们进行优化。

比如说:当你做列表页性能优化时,开始布局时并没有确定列表页图片的初始尺寸,只设定了一个基础的占位尺寸。那么当图片加载完毕后,主线程才知道图片的大小,不得不重新进行布局计算,然后再次进行页面渲染。

参考文章

  • 《前端性能优化方法与实践》

写在最后

页面加载全过程很复杂,内容也比较多,能够进行优化点也是众多,而本篇文章只是简单介绍了前端领域的可优化点。对于偏硬件领域能够做的优化点有GPU绘图、操作系统GUI和LCD显示等;对于计算机网络中的网络层和服务层,比如拥塞预防、负载均衡和慢启动;还有一些页面的解析和渲染算法,比如解析算法、标记算法和树构建算法等。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改