前端性能优化策略

本文将从三部分展开介绍,逐步为读者呈现一套清晰的前端性能优化方案。

1、介绍一套合理有效的参考模型(RAIL性能模型)

2、给出一套实用的性能测试手段

3、给出基于此标准的性能提升方案(结合我们自己项目中的业务实例)

一、参考模型

web性能优化,这是大家耳熟能详的东西了。一说到性能优化,大家可能马上就会想到从一些时间节点切入,例如:

首字节时间

白屏时间

首屏时间

用户可交互时间

DOMContentLoaded时间(页面所有内容解析完成时触发)

onLoad时间(等待页面所有资源加载完成时触发)

不同的人会有不同的衡量标准,有的比较重视白屏时间,有的比较关注首屏时间,这并非是完全一致的。

而除此之外,我们可能会考虑其他方面的性能优化问题,例如:

DOM渲染

60FPS动画

Javascript Benchmarks (JS基准)

手段不少,标准也很多,从这些方向着手,都会起到一定的效果,但问题在于众多的标准往往就会导致没有标准

我们几乎没有人可以把时间完全投入在优化上,因此,我们需要一个标准,来告诉我们现在要去优化什么东西,或者什么东西暂时不需要优化。

想要建立一套合理有效的参考模型,就不得不谈及性能优化要解决的痛点。那就是慢

所有的手段都是为了规避慢的体验

比如,一个DOM操作很慢、一个网页加载很慢、加载一个<script>很慢、JS动画很慢、一个20ms的渲染很慢,等等。

慢有很多场景,但这个慢的定义,无法独立于当前操作的上下文。不同的操作需要不同的数量级时间,脱离操作的上下文谈快慢没有任何意义。

同样是500ms:

在加载一个几十KB的文件,用时0. 5s,用户会觉得没有问题。

而如果一个tap操作,要等0 .5s才响应,那估计用户可能就无法容忍。

所以,什么是「 慢 」,其实就是用户觉得这个操作怎么样,用户在当前场景环境下的操作是否让他们自己感受到「 慢 」

对此,Chrome团队提出了一个以用户为中心的性能模型(即RAIL模型)

它基于收集到的用户行为,致力于将性能优化的投入最大化转为产出。

Chrome团队将这些行为分为以下四类:

  1. 响应速度(Response
  2. 观察动画 (Animation)
  3. 浏览器空置状态(Idle)
  4. 资源加载(Load)

这也是RAIL模型的雏形,其名称取自以上4个单词的首字母。

我们知道,每个网络应用均有与其生命周期相关的这四个不同方面,并且这些方面以不同的方式影响着性能。                         

在开始详细谈这个模型之前,有必要先看一下Chrome团队提出的一个关于web站点的体验性参考标准。

因为RAIL模型的指标依赖这些标准。                         

从这张表中我们可以提取一些重要的数值:

0~16ms(渲染一帧的合理时间)

0~100ms(作出响应的合理时间)

1000ms(重要内容的理想加载时间)

最后两项可以不用关注,因为后两项在任何场景都已经超出了良好性能的范畴。

接下来让我们依次来分析

1、Response(响应速度)

如果用户点击了一个按钮,需要保证在用户察觉出延迟之前就得到反馈。

无论是表单控制还是执行动画,只要有输入,这个原则都适用。但如果没有在合理的时窗内完成响应,也就是采取动作和得到响应之间出现了断层,用户将会察觉到这个延迟。

因此,我们必须在 100ms 以内对用户的输入做出响应,否则用户会感到延迟。这适用于任何输入,不管是在单击按钮、切换表单组件,还是在开启一个动画。

可以在不阻塞用户的交互的前提下充分利用这个 100ms 做一些预计算。

2、Animation(观察动画)

动画现在是应用的一大支柱,从滚动到视图变化,都有动画的身影。

我们要明确在这段时间里能做些什么,因为用户可能常常是直接操作,帧率的改变很容易被察觉到。但是用户想要的只是流畅的响应而已。

当前大多数设备的屏幕刷新频率都是 60次/秒 ,因此我们的目标就是要在1秒内产生60帧。

一般渲染一帧会经历的过程

这样的话,从纯粹数学的角度上来看,每一帧仅有 16.66ms(我们按 16ms 来算) 的时间预算。

但实际上,在渲染某一帧画面的同时,浏览器还有一些额外的工作要做(比如渲染队列的管理,渲染线程与其他线程之间的切换等等)。因此单纯的渲染工作,一般需要控制在 10ms 之内完成,才能达到流畅的视觉效果。如果超过了这个时间限度,页面的渲染就会出现卡顿效果。

3、IDLE(空置状态)

要制作响应迅速动画精良的应用通常需要比较长的工时。并非所有工作都要在 response 阶段或者 load 阶段完成:评论引导、组件初始化、数据检索和排序和分析数据不是需要立刻传达给用户的,所以可以在浏览器空闲的时候再处理这些任务。

我们应该最大化空闲时间,然后利用空闲时间来完成一些延迟任务。比如,最小化预加载数据,以保证应用程序快速加载完成,然后我们就可以用空闲时间来加载剩余的数据。

延迟任务应该按 50ms 进行分组。因为最高优先级的工作是在 100ms 内响应用户任何的输入。为了保证< 100ms 的响应,应用程序必须在每个<100ms的时间内将控制权交回主线程,这样主线程才能响应用户输入。

50ms 块工作既可以完成任务,又能确保即时的响应。

4、LOAD(资源加载)

页面加载时间是最常见的性能话题。对用户来说最先看到的内容应该是最有意义、最先被加载出来的。

接着页面要持续响应用户,绝对不允许出现在滚动页面、轻触或者看动画的时候卡顿。

根据上面的体验性参考标准,我们要在1s内加载完成页面,如果没有,用户会感到迷惑,随后他们对待这个加载任务的感觉就会被破坏。实际上我们并不需要在1s内加载完所有东西,只需要在1s内完成关键呈现路径即可,这样就可以给用户提供一个全部加载完成的错觉。比如可以搞一些渐进式渲染,或在后台做一些任务、延迟一些非关键加载到空闲时间中,这些都是一些比较常见的优化手段。

总结我们刚刚谈到的几点,可以把性能模型的指标归纳如下表:                         


二、性能测试手段

首先,我们根据前文,确定以用户体验为中心的性能衡量标准。然后,针对这些性能标准,来介绍一些相应的性能测试手段来评估和达到性能优化的目的。

工具:Lighthouse,建议作为 Chrome 扩展程序运行,方便读取报告。

1、FP/FCP

FP (First paint)  页面首次绘制时间

首次绘制:包括了任何用户自定义的背景绘制,它是首先将像素绘制到屏幕的时刻。

FCP (First contentful paint) 是 首次内容绘制

首次内容绘制:浏览器将第一个 DOM 渲染到屏幕的时间。该指标报告了浏览器首次呈现任何文本、图像、画布或者 SVG 的时间。

这两个指标指示了我们通常所说的 白屏时间

在控制台查看 paint 性能:

window.performance.getEntriesByType('paint') 

我们可以在代码中查看 paint 性能:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.entryType)
    console.log(entry.startTime)
    console.log(entry.duration)
  }
})

observer.observe({entryTypes: ["paint"]})

2、FMP

FMP(First meaningful paint),首次有意义的渲染,对FMP的测量有助于我们完成RAIL模型中「 最重要的内容要在1000ms内展示给用户 」的要求。

通常,很难有一个通用标准来指示所有的页面当前时刻的渲染达是否到了有用的程度,所以当前并没有制定标准。对于开发者,我们可以根据自己的页面来确定那一部分是最重要的,然后度量这部分渲染出的时间作为FMP。

测试FMP

1、chrome 提供的性能分析工具 Lighthouse 可以测量出页面的 FMP。

Lighthouse 使用的算法是:页面绘制布局变化最大的那次绘制(根据 页面高度/屏幕高度 调节权重)

原文如下:

First meaningful paint = Paint that follows biggest layout change

layout significance = number of layout objects added / max(1, page height / screen height)

参考:Time to First Meaningful Paint: a layout-based approach

2、自主测试:使用​Performance.mark() ​方法,在浏览器的性能缓冲区中使用给定名称添加一个 ​timestamp(时间戳)​

  • 创建一些标记
performance.mark("squirrel")

performance.mark("monkey")

performance.mark("dog")
  • 获取所有的 PerformanceMark 条目
// 获取所有的 PerformanceMark 条目。
const allEntries = performance.getEntriesByType("mark")
console.log(allEntries.length) // 6

// 获取所有的 "monkey" PerformanceMark 条目。
const monkeyEntries = performance.getEntriesByName("monkey")
console.log(monkeyEntries.length) // 2

// 删除所有标记。
performance.clearMarks()
  • 测量关键图片加载完成时间
<img src="corp.jpg">
<script>
    performance.mark("img displayed")
</script>
  • 测量文字类元素加载完成时间
<p>
    This is the call to action text element.
</p>
<script>
    performance.mark("text displayed")
</script>

3、Long Tasks

单线程的JS用事件循环的方式来处理各个事件。

事件任务被浏览器放入事件循环队列中,单线程会逐个处理事件循环队列中的任务。

如果有一个任务消耗了特别长的时间,就会阻塞队列中的其他任务。

同时 js 线程和 ui 渲染线程是互斥的,执行JS会阻塞ui渲染。此时,用户在使用时将会感受到卡顿和闪烁,这是当前 web 页面不好的用户体验的主要来源。

                         

测试 Long tasks

const observer = new PerformanceObserver(function(list) {
  for (const entry of list.getEntries()) {
    console.log({
      eventCategory: 'Performance Metrics',
      eventAction: 'longtask',
      eventValue: Math.round(entry.startTime + entry.duration),
      eventLabel: JSON.stringify(entry.attribution),
    })
  }
})

observer.observe({type: "longtask", buffered: true})

4、TTI

TTI ( time to interactive ) ,(持续)互动时间,指的是应用既完成了视觉上的渲染,又可以响应用户输入(并且随后有5秒网络和主线程不活动时间)的时刻。                         

TTL 作为一种度量标准,主要描述的,是页面或应用程序,何时包含有用的内容,以及主线程何时空闲并自由响应用户交互(可维持5秒流畅交互)。                         

互动时间作为一种性能指标,突出显示了JavaScript主线程空闲数秒的时刻,并基于反映出网站或应用程序与之交互时的效率。由于它依赖于主线程,因此在很大程度上取决于JavaScript的数量以及给定设备的CPU速度。应用不能响应用户输入的原因主要包括:

  1. 使得页面上的组件能工作的 js 还未加载
  2. 长任务阻塞了主线程

TTI 指明了页面的 js 脚本都被加载完成且主线程处于空闲状态 的时间

测量TTL:chrome 提供的性能分析工具 Lighthouse 可以测量出页面的 TTL。


三、性能提升方案

我们掌握了用户为中心的性能量化标准,是为了指导我们优化性能的方向。

1、优化 FP/FCP

  1. 在 ​<head>​ 移除影响 FP/FCP 的 css 和 js 代码
  2. 将影响首屏渲染的关键 css 代码最小集合直接 inline 写在 ​<head>​ 中
  3. 对 react 这种客户端渲染框架,做 ssr
  4. 本地缓存

2、优化 FMP/TTI

  1. 首先需要确定页面中的最关键元素,例如上文中的视频组件,然后需要保证关键组件相关的代码最先加载并且使得关键组件在第一时间被渲染且可交互
  2. 图片和组件懒加载
  3. 延缓加载一些对渲染关键组件无用的代码
  4. 减少 html dom 个数和层数
  5. 尽量缩减 FMP 和 TTI 的时间间隔,最好让用户知道当前页面并未完全可交互。避免用户想要交互,但是页面没有响应的情况。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值