写在前面
本文首发于公众号:符合预期的 CoyPan
首先贴一下参考文章的地址:
https://developers.google.com…
最近这段时间,我在做 h5 的首屏加速相关的工作。首先需要搞清楚的问题就是:首屏加速,到底是要加速什么? 答案可能很简单:加快网页的展现过程。不过再细想一下,网页快不快是针对用户而言的,那么什么样的速度会让用户感到快呢?或者说,哪些指标能够衡量用户所感知的 ” 快 ” 呢?
首屏性能指标
我首先想到的衡量指标就是网页开始请求,到页面渲染完成展现在用户眼前,这中间的时间差。现代浏览器都提供了 window.performance
的 api,可以通过 window.performance.timing.fetchStart
来获取网页开始请求时候的时间戳。页面渲染完成的时间点,可以在页面的业务逻辑中判断。我用这种打点方式统计了业务中某页面的首屏加载时长:
(上图中,横坐标是加载耗时,单位是毫秒。纵坐标是各个加载耗时对应的 pv 数)
上图的数据中,pv 总数为 90316,首屏加载时长 60 分位值为:1394ms,90 分位值为:2715ms。也就是说,60% 的用户能在 1394 毫秒内看到完整的页面,90% 的用户能在 2715 毫秒内看到完整页面。
在测量首屏时长的时候,为了看到长尾数据的影响,一般会采用分位值的方式,平均数会用来作为参考,毕竟平均数在样本量不够大的情况下,受极大、极小值的影响比较大(比如我和马云平均年薪几个亿)。
首屏性能指标细化
上面的数据中,我只是简单的统计了页面整体的首屏耗时。但其实页面加载是一个很复杂的过程。在加载过程中,哪些指标可能会影响到用户的体验呢?谷歌给出了以下的指标:
- FP:首次绘制。用于标记导航之后浏览器在屏幕上渲染像素的时间点。这个不难理解,就是浏览器开始请求网页到网页首帧绘制的时间点。这个指标表明了网页请求是否成功。
-
FCP:首次内容绘制。FCP 标记的是浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG 甚至
<canvas>
元素。 - FMP:首次有效绘制。这是一个很主观的指标。根据业务的不同,每一个网站的有效内容都是不相同的,有效内容就是网页中 ” 主角元素 ”。对于视频网站而言,主角元素就是视频。对于搜索引擎而言,主角元素就是搜索框。
- TTI:可交互时间。用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点。应用可能会因为多种原因而无法响应用户输入:①页面组件运行所需的 JavaScript 尚未加载完成。②耗时较长的任务阻塞主线程
下面是参考文章中的一个例子,来直观表示上述的四种指标。
怎么测量 FP、FCP、FMP、TTI 的值
明确了上述的几个指标的含义后,下面介绍一下如何测量这几个指标的值。
FP 和 FCP
现代浏览器已经为我们提供了性能测试的 api。直接上代码:
- PerformanceObserver
在页面 html 代码顶部加入以下代码,订阅性能事件。
const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
console.log(entry);
console.log(metricName + ':' + time);
}
});
observer.observe({entryTypes: ['paint']});
上述代码的输出如下:
- performance
下面的代码也是可以的:
window.performance.getEntriesByType('paint');
FMP
FMP 是一个十分主观的指标,需要开发者自己去测量。像文章一开始说的那样,我们可以在业务逻辑中判断页面加载、渲染完成时记录一个时间戳,然后和 window.performance.timing.fetchStart
相减,来得到 FMP。
TTI
TTI 也是一个较为主观的指标。浏览器并没有为这个指标提供 api。不过 google 提供了 polyfill。下面是使用方法。
在页面 html 顶部加入:
!function(){if('PerformanceLongTaskTiming' in window){var g=window.__tti={e:[]};
g.o=new PerformanceObserver(function(l){g.e=g.e.concat(l.getEntries())});
g.o.observe({entryTypes:['longtask']})}}();
install 这个 polyfill,
npm install tti-polyfill
在业务代码中引用:
import ttiPolyfill from 'tti-polyfill';
...
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {console.log('tti', tti);
});
...
getFirstConsistentlyInteractive()
方法接受可选的startTime
配置选项,让您可以指定下限值(您知道您的应用在此之前无法进行交互)。默认情况下,该 polyfill 使用 DOMContentLoaded 作为开始时间,但通常情况下,使用主角元素呈现的时刻或您知道所有事件侦听器都已添加的时间点这类时间会更准确。
如何优化
如何优化首屏速度?一提到这个 topic,我首先想到的就是一系列写代码时应注意的原则,以及以下几个点:
1、充分利用缓存。
2、资源懒加载。
3、http 请求层面的优化,比如上 http2。
4、如果是客户端内的页面,可以做预加载等。
5、SSR。
….
本文不会深入去讨论每一种方法具体应该怎么实施。在了解了文章中提到的几个关键性能指标后,可以针对页面加载的每一个阶段,进行深入的数据采集,分析和研究。在研究过程中,自然可以知道页面到底慢在哪里,不同的阶段使用不同的方法进行优化。回到文章的标题,当考虑网页首屏速度优化时,我们在考虑什么?其实就是在考虑通过一系列的方法,缩小网页的 FP、FCP、FMP、TTI,以给用户提供良好的体验。
写在后面
本文根据谷歌的参考文章,对页面加载过程中的几个重要时间点进行了总结,明确、细化了首屏优化的方向和思路,符合预期。