前端性能优化那些事

本文原创:gaoruixiacss

一. 开篇

提起前端性能优化,你们会想到哪些内容呢?html

相信不一样的人给出的答案不一样,关于性能优化没有标准答案,它更像是一个摸索的过程,本篇文章给出从页面加载的各个过程分析可优化的点前端

二. 经常使用性能优化指标

下面这张图是一个页面从打开到加载完成经历的各个阶段webpack

webLoadTiming.png

  • 浏览器得到各个阶段的时间耗时的APIweb

    - Timing标准:window.performance.timing
    - Timing2标准: window.performance.getEntriesByType('navigation')[0]。
    复制代码

    Timing目前浏览器已再也不提供维护支持,Timing2获取的精度更加准确(比飞秒还精确,为10的-17次方秒),但兼容性尚且没有那么好(主要是iOS11才开始支持,其余浏览器都基本没问题了,IE9咱们就让它见鬼去吧)。在京麦插件性能统计中,咱们优先判断是否支持后者,除非后者不支持才使用前者。ajax

  • 白屏时间json

    从navigatorStart到responseEnd这段时间算做服务器时间
    从浏览器开始加载页面到首次出现内容以前的这段时间(从页面发送一个页面URL请求出去,到服务器返回这个HTML的文本内容)后端

    计算方式
    - Timing: performance.timing.responseEnd - performance.timing.navigationStart
    - Timing2: performance.getEntriesByType('navigation')[0].responseStart
    复制代码
  • 浏览器渲染时间浏览器

    从responseEnd到loadEventEnd这段时间,包括CSS、JavaScript、Image等资源的加载耗时缓存

    计算方式
    - Timing: performance.timing.loadEventEnd - performance.timing.responseEnd
    - Timing2: performance.getEntriesByType('navigation')[0].loadEventEnd - performance.getEntriesByType('navigation')[0].responseStart
    复制代码
  • 整页时间

    从浏览器开始加载页面到整个页面加载完毕(最明显的标识就是移动端浏览器进度条读完了,pc端浏览器就是当前页签前面的loading消失了)

    计算方式
     - Timing: performance.timing.loadEventEnd - performance.timing.navigationStart
     - Timing2: performance.getEntriesByType('navigation')[0].loadEventEnd
    复制代码

使用Chrome调试工具performance查看浏览器请求一个页面到渲染完成的过程

能够打开浏览器控制台,切换到Performance,点击刷新按钮,等页面加载完成

webLoad03.jpg

三. 影响整页时间的因素

先了解下获得html文本内容后页面的加载渲染流程(以webkit主流程为例)

webLoad01.jpg

  • 渲染流程有四个主要步骤

    1. 解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树
    2. 构建Render树 - 接下来不论是内联式,外联式仍是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另一棵用于渲染的树-渲染树(Render tree),
    3. 布局Render树 - 而后对渲染树的每一个节点进行布局处理,肯定其在屏幕上的显示位置
    4. 绘制Render树 - 最后遍历渲染树并用UI后端层将每个节点绘制出来

    以上步骤是一个渐进的过程,为了提升用户体验,渲染引擎试图尽量快的把结果显示给最终用户。它不会等到全部HTML都被解析完才建立并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展现出来。

  • 阻塞渲染的因素

    没有js的理想状况下,html与css会并行解析,分别生成DOM与CSSOM,而后合并成Render Tree,进入Rendering Pipeline; 但若是有js,css加载会阻塞后面js语句的执行,而(同步)js脚本执行会阻塞其后的DOM解析(因此一般会把css放在头部,js放在body尾)

    • CSS的阻塞状况

      • css不会阻塞DOM树的解析
      • css会阻塞DOM树的渲染
      • css会阻塞后面js语句的执行
    • JS的阻塞状况

      • JS阻塞DOM解析,但浏览器会"偷看"DOM,预先下载相关资源
      • 浏览器遇到script且没有defer或async属性的标签时,会触发页面渲染,于是若是前面CSS资源还没有加载完毕时,浏览器会等待它加载完毕在执行脚本

webLoad02.jpg

蓝色线表明网络读取,红色线表明执行时间,这俩都是针对脚本的;绿色线表明HTML解析
复制代码
  • 由上能够得出影响整页时间的因素

    1. CSS、JS、图片等资源加载
    2. CSS、JS文件放置位置,避免阻塞DOM解析和渲染
    3. ajax同步请求
    4. jsonp请求
    5. 页面加载过程当中JS发送图片请求

四. 优化分析

使用Chrome调试工具performance

下图中,在NetWork资源加载瀑布图中咱们能够查看是主要是哪些资源卡住了页面的整页时间,发送结束时间均可以观察到。以后再根据哪一个请求是瓶颈,对那个请求进行分析

webLoad04.jpg

使用Chrome插件pagespeed和Lighthouse

  • 使用插件pagespeed

    首先去安装,安装完pagespeed以后,打开你要调试的网页,打开控制台的pagespeed,而后点击左上角的ANALYZE按钮,开始分析页面性能状况

pageSpeed.jpg

  • 插件Lighthouse相似,这里不详细介绍了

  • 对于整页时间较长的状况,须要分析具体是在哪一个阶段致使整页时间比较长,能够从上面整页时间的说明里的各个阶段去分析

  • 整页时间主要在dom解析耗时上,这段时间是从HTML开始解析,到JS所有执行完毕,这之中包含了CSS、JS等资源的加载,须要去优化HTML解析这段时间。

五. 各阶段优化手段

经常使用性能优化指标咱们知道了页面加载总共分红下面这几个阶段:

重定向时间 -> DNS缓存查询时间 -> DNS查询时间 -> TCP链接时间 -> Request请求时间 -> Response请求响应时间 -> DOM解析时间 -> DOM渲染时间 -> Load事件执行时间

下面来看下各阶段的优化手段

1. 重定向时间

这个很简单,就是请求到正确的地址上便可。
常常会出现的状况是:

  • 将index.html的文件地址请求写成这样:https://xx.com/xx,这样会致使浏览器重定向到https://xx.com/xx/
  • 将http请求重定向到https上 这种状况注意下便可

2. DNS缓存查询时间

这个是浏览器作的处理,如Chrome浏览器对DNS的缓存时间是1分钟。

这个阶段咱们没法优化。

3. DNS查询时间

HTML的请求的DNS查询时间咱们没法缩短,但对于里面的其余域名的请求,咱们能够提早进行DNS查询,减小资源或者接口的请求时间。

在HTMl的头部head中加上DNS预查询便可

<link rel="dns-prefetch" href="//example.com">

通常对CDN须要用到的域名作解析便可。如下是CDN的各个域名:

4. TCP链接时间

TCP链接时间主要在3次握手中,缩短这个时间就是拉近用户和服务器机房的距离,对于接口的请求这个很难作到,但对于静态资源的TCP链接时间,咱们能够经过CDN全国节点缩短用户与服务器之间的距离。

另外,咱们还能够用HTTP2的keep-alive来保持长链接,这个CDN服务器默认是开启的,对于接口服务器也能够进行开启,虽然异步Ajax请求并不影响整页时间,但能让用户早点看到内容,何乐而不为。

5. Request请求时间 和 Response请求响应时间

在Request请求中,请求返回的时间取决于服务器的处理时间。

对于先后端耦合的项目或者SSR的项目,由于须要处理逻辑,拿出数据再动态构建HTML文本,以后再将HTML文本返回给浏览器,那这段时间主要在于处理逻辑上。

对于先后端分离的项目,由于返回的是一个空的HTML文本,数据都是等JS调用Ajax获取的,因此HTML的Request请求时间很短。但这样咱们得处理Ajax请求,它也有Request请求的时间,这个也得看怎么在服务端进行性能提高。

虽然Ajax请求不计算在整页时间中,但也别为了缩短整页时间而选择先后端分离,这个并非它的优点所在。用户关心的是页面何时加载完,这其中包含数据何时展示,因此别为了整页时间的优化而优化,而应该关心用户体验,就算是Ajax接口也应该考虑怎么提高性能。

并且使用先后端分离其实会加长页面的白屏时间,这个也是一个衡量页面性能的一个重要指标,白屏时间能够经过添加骨架屏来优化。

对于静态资源的请求,通常资源越小,请求越快(也受服务器带宽的影响)。能够经过减少静态资源(JS、CSS、图片等)请求的大小从而来缩短请求响应时间。

6. DOM解析时间

这段时间从服务器响应返回HTML文本,浏览器开始按照从上到下的执行顺序解析HTML文本,到执行完HTML中的全部JS,这段时间咱们将其称为DOM解析时间。因此优化这段时间在于缩短HTML中的CSS文件和JS文件的请求时间和执行时间,以及缩短HTML的解析时间。

缩短CSS文件和JS文件的请求时间

能够从如下几方面来作

  • 静态资源放在CDN上
  • 压缩文件(代码混淆压缩,Gzip)
  • js文件拆分按需加载(webpack合理配置, 可以使用webpack-bundle-analyzer来分析打包)

缩短HTML的解析时间

  • 从前面影响整页时间的因素中咱们能够知道,CSS会阻塞HTMl的渲染和JS的执行,JS会阻塞HTML的解析和渲染,所以为了不阻塞,要严格遵照CSS文件放在head头部,JS文件放在body尾部
  • 减小DOM树的嵌套深度,这个属于代码层次的细节优化,通常在于代码书写习惯上,这个很差优化
  • 优化JS代码执行逻辑,避免耗时处理,必要时可使用Web Worker开启多线程

7. DOM渲染时间

这个时间就在于减小重排(页面布局变更)和重绘(页面从新渲染元素样式)

减小重排和重绘的手段

  • JS 中 CSS 属性读写分离
  • 切换 class 或者 style.csstext 属性来批量修改样式
  • 将没用的元素设为不可见
  • 压缩 Dom 的深度,多使用伪元素或者 box-shadow
  • 指定 img 标签的大小
  • 使用独立渲染层(触发新的渲染层的方式或元素:Video元素,WebGL,Canvas,CSS3 3D,CSS滤镜,z-index大于相邻节点)
  • DOM 元素离线更新(使用appendChild和Document Fragment)

8. Load事件执行时间

这个时间的压缩就是减小onload回调函数的耗时处理

不少人习惯将事件绑定放在onload事件中,实际上是能够将其提早,放在尾部JS中直接执行或者DOMContentLoaded事件中,这时候DOM解析完成了,已经能够获取到DOM节点了

总结

本文从页面加载的各个阶段分析影响因素,简单的给出了可优化的方向,具体实施还须要在工做中结合团队现状来进行

相关文章
相关标签/搜索