web性能优化,这是大家耳熟能详的东西了。
一说到性能优化,大家可能立马就会想到和加载相关的时间了,比如首字节时间、白屏时间、首屏时间、用户可交互时间、DOMContentLoaded时间、onLoad时间等等,但是不同的人也许会有不同的衡量标准,比如有些人比较重视白屏时间,有的比较关注首屏时间等,这并非完全一致的。当然我们可能还会考虑其他方面的性能优化问题,比如DOM渲染、60FPS动画、benchmarks等,但是我们应该什么时候去做优化呢?一直做?所有都做?这可能有点不切实际。
我们大部分人都没有足够的时间投入到优化工作中,我们需要一个权威的标准来告诉我们哪些重要的事情是必须优化的和哪些次要的。
对此,Chrome团队提出了一个以用户为中心的性能模型:RAIL。
在我们深入了解RAIL之前,我们先回想一下,我们为什么要做性能优化?其实无非就是一个字,“慢”!
一个DOM操作很慢?一个网页加载很慢?在<head>
中加载一个<script>
很慢?JavaScript动画很慢?一个20ms的操作很慢?那么0.5s或者10s呢?…
显然不同的操作是需要不同数量级时间的,脱离了这个操作的上下文谈快慢是没有任何意义的。当你在加载一个几十kb的文件时,0.5s也许用户觉得没有问题,但是如果是一个tap操作要等0.5s才响应,那估计用户早就掀桌子走人了。
所以“什么是慢?”,其实就是用户觉得这个操作怎么样,毕竟我们的站点是为用户而建的。
这就是RAIL所谓的“Focus On The User”。
Focus On The User
时至今日,web已经不是以前那种简单的静态页面了,我们的站点越来越多样化,对交互体验要求越来越高了,我们会想方设法地提高站点的用户体验,以期望留住更多用户。
什么样的体验才是好的呢?一个重要的准则就是要让用户忙碌起来,我们不能让用户等待!
其实很早之前,人们就一直在探索web站点的体验性,这个调研就提出了响应时间的3个重要限制,分别是0.1s、1s、10s;然而现在已经是2016了,我们当然不会再以这3个临界值为标准了,Chrome团队提出了一个参考标准:
延时 | 用户行为 |
---|---|
0-16ms | 对于一个60fps刷新频率的屏幕,这个时间范围代表了屏幕渲染一个单独帧的时间(1000/60~=16ms)。人们非常擅长跟踪运动,他们不喜欢那些不符合预期的运动,也不喜欢可变帧率和周期性停止。 |
0-100ms | 在这个时间范围内向用户做出响应,他们会感觉到这样的结果是即时的。超过100ms,行动和反应之间的连接就会被破坏。 |
100-300ms | 用户会感受到一个轻微的可察觉的延迟。 |
300-1000ms | 在这个时间范围内,事情看起来是断断续续的。对web上得大多数用户,加载页面和改变视图都代表了一个任务。 |
1000+ms | 超出1s,用户会渐渐地对他们正在执行的任务失去耐心。 |
10000+ms | 用户会绝望的,然后放弃任务,并且不会再爱了。 |
这些感知阀值有很有意义的,因为他们给了我们所需要的判断基石。
对于某个操作,时间超出了某个阀值,用户就会觉得这个操作慢了!
实际上,一个用户访问一个站点,通常都会有以下行为:
- 等待资源加载
- 观察动画
- 滚动页面
- 点击一个图标
- …
Chrome团队将这些行为分成了4类:Response、Animation、Idle、Load,这就是所谓的RAIL,名称取自以上4个单词的首字母。
(图片来自Google Developers)
The RAIL Performance Model
RAIL认为我们的终极目标就是要让用户用得爽,如果我们按照每个分类的性能目标(参考以上表格)进行优化,那么用户就会爽!
下面我们就对每一个类别进行分析。
Response
我们必须在100ms以内对用户的输入做出响应,否则用户会感到延迟。这适用于任何输入,不管他们是在单击按钮、切换表单组件,还是在开启一个动画。
如果没有及时响应,行动和反应之间的连接就会被破坏。用户就会感觉到延迟。
一般情况下,我们在用户初始交互之后,例如一个tap操作,100ms内就得给用户提供一个反馈,否则用户可能会疑惑自己的操作是否生效了,尽管这个反馈可能不是用户所期望的最终结果,比如任务计算时间比较长,我们可能会提供给用户一个loading指示或者一个active状态等。
chrome团队还认为,实际上我们还可以充分利用这个100ms做一些预计算,只要不阻塞用户的交互就行。
Animation
动画实际上是web应用程序一个不可避免的行动。比如,scrolling和touchmoves都是动画。如果动画的帧率是变化的,我们的用户可以真切地感受到。 当前大多数设备的屏幕刷新频率都是60次/秒,因此我们的目标就是要在1秒内产生60帧,而每一帧一般都会经过以下步骤:
(图片来自Google Developers)
从纯粹数学的角度上来看,每一帧其实都有16.66ms的时间预算。
但实际上,在渲染某一帧画面的同时,浏览器还有一些额外的工作要做(比如渲染队列的管理,渲染线程与其他线程之间的切换等等)。因此单纯的渲染工作,一般需要控制在10毫秒之内完成,才能达到流畅的视觉效果。如果超过了这个时间限度,页面的渲染就会出现卡顿效果,也就是我们常说的jank,它是非常糟糕的用户体验。
更多内容可以参考渲染性能。
Idle
我们应该最大化空闲时间,然后利用空闲时间来完成一些延迟任务。比如,最小化预加载数据,以保证应用程序快速加载完成,然后我们就可以用空闲时间来加载剩余的数据。
延迟任务应该按50ms进行分组。为什么?因为最高优先级的工作是在100ms内响应用户任何的输入。
为了保证<100ms的响应,应用程序必须在每个<100ms的时间内将控制权交回主线程,这样主线程才能响应用户输入。
在每个50ms时间片段内工作,可以保证延迟任务的完成,同时也能即时响应用户操作。
Load
我们要在1s内加载完成我们的站点!如果没有,用户就会很迷惑,然后他们对待这个加载任务的感觉就会被破坏!
实际上我们并不需要在1s内加载完所有东西,我们只需要在1s内完成关键呈现路径即可,这样就可以给用户提供一个全部加载完成的错觉!开启渐进式渲染并且在后台做一些任务、延迟一些非关键加载到空闲时间中,这些都是一些比较常见的优化手段。
更多内容,可以参考优化关键呈现路径。
小结
本文只是简单地介绍了一下chrome团队的RAIL性能模型,它为我们的性能优化工作提供一个参考模型,我们所要做的就是参照这个模型,利用Chrome的Timeline工具来进行性能优化。