前端性能优化之从URL输入到页面加载过程分析
本文转载自微信公众号「前端万有引力」,作者一川 。转载本文请联系前端万有引力公众号。
1写在前面在页面加载到最终渲染显示大致是这样的:用户在浏览器输入URL回车后,浏览器为了将URL解析成IP地址,会向DNS服务器发起DNS查询,获取IP地址。在建立连接后,浏览器就可以发起HTTP请求,而服务器接受请求后进行响应,浏览器从响应结果中拿到数据,并进行解析和渲染,最后在用户面前就出现了一个网页。简而言之就是三个阶段:
客户端发起请求阶段 服务端数据处理请求阶段 客户端页面渲染阶段 2客户端请求阶段的优化点客户端发起请求阶段是指用户在浏览器输入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请求,减少访问的时间、提升效率和速度。
3服务端数据处理阶段的优化点服务端数据处理阶段指的是WevServer接受到请求后,从数据存储层取到数据,再返回给前端的过程。服务端程序接受到HTTP请求后,会做一些请求参数处理以及权限校验。此过程的优化点:在于是否做了数据缓存处理、是否做了gzip压缩以及是否具有重定向。gzip压缩是一种压缩技术,通过gzip压缩资源的下载速度会快很多,能够大大提升页面的展示速度。
数据缓存
在进行数据缓存的几种方法:
借助Service Worker的数据接口缓存 借助本地存储的接口缓存 CDNService Worker:是浏览器的一个高级属性,本质上是一个请求代理层,它存在的目的就是拦截和处理网络数据请求。星空app
借助本地存储的接口缓存:指的是在一些对数据时效性要求不高的页面,第一次请求到数据后,程序将数据存储到本地存储。下一次请求的时候,先去缓存里面取出数据,如果没有的话再想服务器发起请求。
CDN:基本思路是通过在网络各处放置节点服务器,构造一个智能虚拟网络,将用户的请求导向离用户最近的服务节点上。
为什么数据缓存会成为性能的优化点呢?这是因为每次请求数据接口,需要从客户端到后端服务器再到更后端的数据存储层,一层一层返回数据,最后再返回客户端,这样请求响应的耗时很长。
重定向
重定向是指网站资源迁移到其他位置后,用户访问站点时,程序会自助将用户请求从一个页面转移到另外一个页面的过程。重定向的三种方式:
服务端发挥的302重定向 META标签实现的重定向 前端Javascript通过window.location实现的重定向它们都会引发新的DNS查询,会导致新的TCP三次握手和TLS协商以及产生新的HTTP请求,而这些都会导致请求过程中更多地时间,进而影响前端性能。
当前服务端对数据加工聚合处理后,客户端拿到数据,接下来会进入解析和渲染阶段。解析阶段就是HTML解析器将页面内容转换成DOM树和CSSDOM树的过程。所谓DOM树,就是文档对象模型(Document Object Model),它描述了标签之间的层次和结构。CSSDOM树,即CSS对象模型,主要描述了样式集的层次和结构。
CSS解析器遍历其中每个规则,将CSS规则解析浏览器可解析和处理的样式集合,最终结合浏览器里面的默认样式,汇总形成具有父子关系的CSSDOM树。
4页面解析和渲染阶段的优化点主线程会计算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,如果元素量大,计算出所有的元素位置和尺寸会花费很长的时间,所以布局阶段很容易成为性能瓶颈点,需要我们进行优化。
比如说:当你做列表页性能优化时,开始布局时并没有确定列表页图片的初始尺寸,只设定了一个基础的占位尺寸。那么当图片加载完毕后,主线程才知道图片的大小,不得不重新进行布局计算,然后再次进行页面渲染。
5参考文章《前端性能优化方法与实践》
6写在最后
页面加载全过程很复杂,内容也比较多,能够进行优化点也是众多,而本篇文章只是简单介绍了前端领域的可优化点星空入口。对于偏硬件领域能够做的优化点有GPU绘图、操作系统GUI和LCD显示等;对于计算机网络中的网络层和服务层,比如拥塞预防、负载均衡和慢启动;还有一些页面的解析和渲染算法,比如解析算法、标记算法和树构建算法等。
星空入口 星空官网