RAIL 性能评估模型,Chrome Devtools 性能监测的功能,lighthouse 性能报告,webpagetest 在线性能分析,以及监测性能的 Performance API ……
一 RAIL 性能评估模型
RAIL 是一种以用户为中心的性能模型,代表 Web 应用程序生命周期的四个不同方面:响应、动画、空闲和加载,用户对其中的每一个都有不同的性能期望。
1. 响应(Response)
用户从输入到响应理想情况下应在 100ms 内,考虑到 idle task 的情况,事件会排队,等待时间大概在50ms,因此事件的处理要在 50ms 内完成,如上图。这种情况适用于click,toggle,starting animations等,不适用于 drag 和 scroll。
要减少事件处理的时间,复杂的 js 计算尽可能放在后端,避免对用户输入造成阻塞。超过 50ms 的响应,要提供反馈,比如倒计时,进度百分比等。
2. 动画(Animation)
要达到视觉上的平滑,需要每秒闪过60个画面(60帧),浏览器渲染一帧的时间大概是 6ms,因此产生每一帧的时间不要超过 10ms。
要达到这样的要求,在一些高压点上,应尽可能地少做事,如不要取 offset、设置 style 等 js 操作。
3. 空闲(Idle)
最大化空闲时间,以增大 50ms 内响应用户输入的几率。
用空闲时间来完成一些延后的工作,如先加载页面可见的部分,然后利用空闲时间加载剩余部分,此处可以使用 requestIdleCallback API。
在空闲时间内执行的任务尽量控制在 50ms 以内,如果更长的话,会影响 input handle 的 pending 时间。
如果用户在空闲时间任务进行时进行交互,必须以此为最高优先级,并暂停空闲时间的任务。
4. 加载(Load)
传输内容到页面可交互的时间不超过 5 秒,第二次打开,尽量不超过2秒。
禁用渲染阻塞的资源,延后加载。可以采用 lazy load,code-splitting 等其他优化手段,让第一次加载的资源更少。
二 Chrome Devtools 的性能监测
Chrome 浏览器的开发者工具功能十分强大,在许多方面引导着 web 的发展。这里只介绍性能监测相关的 Network 和 Performance 的主要功能。其他内容可参考 github.com/CompileYout…
1. Network
如图,对京东网站刷新后的一次监测。长按浏览器左上角的刷新图标,可以选择清除缓存刷新,或者设置 Disable cache 清除缓存。
概要
最下边的红框内是监测内容的概要,共有 160 个请求,网络传输的资源总大小 1.7MB,实际源文件总大小为 2.6MB;页面最后一个请求截止的时间 23.94 秒;DOM 内容加载并解析完成时间,即页面白屏时间 1.93 秒;页面所有的资源加载完成的时间 5.35 秒。(本人网络有点不好)
waterfall
一般人可能不关注这个,实际这个概念很重要。
这一栏有两个竖线,蓝色的竖线表示 DOM 加载完成的时间点,红色的竖线表示所有声明的资源加载完成的时间点。
鼠标悬浮上去,waterfall 显示的内容主要有四块:排队(queued)、网络连接(Connection)、请求响应(request/response)、总耗时(Explanation)。
waterfall 可以看到每个阶段的用时状况,其中最受开发者关注的是Wating(TTFB),它表示从请求发出到请求回来的耗时。
size
资源大小。有两个值,上面那个是网络传输中的资源大小,下面那个是资源源文件的大小,一般传输过程中资源会被压缩以缩减传输时间和流量,所以一般情况下,上面的值会比下面的小。鼠标悬浮在上面可看具体释义。
time
加载资源耗时。
Capture screenshots
捕捉网页截图。截下 DOM 树变化各个重要阶段时的页面,点击某一截屏,可查看该阶段的 network 情况。
2. Performance
通过 performance 可以直观的理解浏览器的渲染原理与工作流程,同时也能够将浏览器的系统架构、消息循环机制、渲染流水线等前端概念联系到一起。
点击图中黑色按钮可以记录交互阶段的性能数据,点击刷新按钮可以记录加载阶段的性能数据,并会生成报告,可以下载保存。生成报告需要一段时间,要耐心等待。
前端开发者一般关注“Main”一行,鼠标放上去通过滚轮放大缩小,可以看到渲染过程等详尽的信息。
Summary(性能摘要)
- Loading :加载时间
- Scripting :js计算时间
- Rendering :渲染时间
- Painting :绘制时间
- Other :其他时间
- Idle :浏览器闲置时间
3. 更多工具
打开开发者工具,Ctrl + Shift + p,可以调出搜索框,搜索查看其他各种功能。
比如 搜索使用 Rendering: Hide frames per second(FPS) meter ,监听动画的帧数。
三 Lighthouse
实际上,Chrome Devtools 集成了 Lighthouse,通过开发者工具可一键生成 Lighthouse 报告。
也可以通过安装 npm 包的方式,使用命令运行 lighthouse 对网站进行评测。
npm install -g lighthouse
lighthouse http://www.baidu.com/
默认情况下,会在执行命令的当前目录下生成一个 HTML 文件,直接打开该文件即可查看报告。
lighthouse 命令的使用有其他很多参数,详细参看官网。
lighthouse 报告指标包含了性能(Performance),访问无障碍(Accessibility),最佳实践(Best Practice),搜索引擎优化(SEO),PWA(Progressive Web App)五个部分,对于前端性能优化来说,主要关注 Performance 部分。
性能 Performance 评分的分值区间是 0 到 100,如果出现 0 分,通常是在运行 Lighthouse 时发生了错误,满分 100 分代表了网站已经达到了98分位值的数据,而 50 分则是 75 分位值的数据。
影响评分的性能指标:性能测试结果会分成 Metrics,Diagnostic,Opportunities 三部分,但只有 Metrics 部分的指标项会对分数产生直接影响,可以作为我们的主要参考点。
Lighthouse 会衡量以下 Metrics 性能指标项:
-
首次内容绘制(First Contentful Paint):即浏览器首次将任意内容(如文字、图像、canvas 等)绘制到屏幕上的时间点。
-
首次有效绘制(First Meaningful Paint):衡量了用户感知页面的主要内容(primary content)可见的时间。对于不同的站点,首要内容是不同的,例如:对于博客文章,标题及首屏文字是首要内容,而对于购物网站来说,图片也会变得很重要。
-
首次 CPU 空闲(First CPU Idle):即页面首次能够对输入做出反应的时间点,其出现时机往往在首次有效绘制完成之后。
-
可交互时间(Time to Interactive):指的是所有的页面内容都已经成功加载,且能够快速地对用户的操作做出反应的时间点。
-
速度指标(Speed Index):衡量了首屏可见内容绘制在屏幕上的速度。在首次加载页面的过程中尽量展现更多的内容,往往能给用户带来更好的体验,所以速度指标的值约小越好,应在4秒以内。
-
输入延迟估值(Estimated Input Latency):这个指标衡量了页面对用户输入行为的反应速度,其基准值应低于 50ms。
另外, Opportunities 指的是优化机会,它提供了详细的建议和文档,来解释低分的原因,帮助我们具体进行实现和改进。Diagnostics 指的是现在存在的问题,为进一步改善性能的实验和调整给出了指导。这两者不会纳入分数的计算。
四 WebPageTest
WebPageTest,是一个可以在线分析 web 性能的工具。
以下是 window 下本地部署 WebPageTest 的步骤:
-
安装 Docker
-
拉取 webpagetest 镜像
docker pull webpagetest/server
docker pull webpagetest/agent
- 启动 docker
docker run -d -p 4000:80 webpagetest/server
docker run -d -p 4001:80 --network="host" -e "SERVER_URL=http://localhost:4000/work/" -e "LOCATION=Test" webpagetest/agent
五 性能测量 API
Navigation Timing(页面加载 Timing) 和 Resource Timing(资源加载 Timing) 可以通过 js 的内置对象 performance API 获取。
1. performance.timing 对象
该对象包含以下属性:
| 属性 | 说明 |
|---|---|
| navigationStart | 准备加载新页面的起始时间 |
| redirectStart | 如果发生了HTTP重定向,并且从导航开始,中间的每次重定向,都和当前文档同域的话,就返回开始重定向的timing.fetchStart的值。其他情况,则返回0 |
| redirectEnd | 如果发生了HTTP重定向,并且从导航开始,中间的每次重定向,都和当前文档同域的话,就返回最后一次重定向,接收到最后一个字节数据后的那个时间.其他情况则返回0 |
| fetchStart | 如果一个新的资源获取被发起,则 fetchStart必须返回用户代理开始检查其相关缓存的那个时间,其他情况则返回开始获取该资源的时间 |
| domainLookupStart | 返回用户代理对当前文档所属域进行DNS查询开始的时间。如果此请求没有DNS查询过程,如长连接,资源cache,甚至是本地资源等。 那么就返回 fetchStart的值 |
| domainLookupEnd | 返回用户代理对结束对当前文档所属域进行DNS查询的时间。如果此请求没有DNS查询过程,如长连接,资源cache,甚至是本地资源等。那么就返回 fetchStart的值 |
| connectStart | 返回用户代理向服务器服务器请求文档,开始建立连接的那个时间,如果此连接是一个长连接,又或者直接从缓存中获取资源(即没有与服务器建立连接)。则返回domainLookupEnd的值 |
| connectEnd | 返回用户代理向服务器服务器请求文档,建立连接成功后的那个时间,如果此连接是一个长连接,又或者直接从缓存中获取资源(即没有与服务器建立连接)。则返回domainLookupEnd的值 |
| requestStart | 返回从服务器、缓存、本地资源等,开始请求文档的时间 |
| responseStart | 返回用户代理从服务器、缓存、本地资源中,接收到第一个字节数据的时间 |
| responseEnd | 返回用户代理接收到最后一个字符的时间,和当前连接被关闭的时间中,更早的那个。同样,文档可能来自服务器、缓存、或本地资源 |
| domLoading | 返回用户代理把其文档的 "current document readiness" 设置为 "loading"的时候 |
| domInteractive | 返回用户代理把其文档的 "current document readiness" 设置为 "interactive"的时候 |
| domContentLoadedEventStart | 返回文档发生 DOMContentLoaded事件的时间 |
| domContentLoadedEventEnd | 文档的DOMContentLoaded 事件的结束时间 |
| domComplete | 返回用户代理把其文档的 "current document readiness" 设置为 "complete"的时候 |
| loadEventStart | 文档触发load事件的时间。如果load事件没有触发,那么该接口就返回0 |
| loadEventEnd | 文档触发load事件结束后的时间。如果load事件没有触发,那么该接口就返回0 |
| 页面性能参数 | 计算方法 |
|---|---|
| DNS查询耗时 | domainLookupEnd - domainLookupStart |
| TCP链接耗时 | connectEnd - connectStart |
| request请求耗时 | responseEnd - responseStart |
| 解析dom树耗时 | domComplete - domInteractive |
| 白屏时间 | responseStart - navigationStart |
| domready时间 | domContentLoadedEventEnd - navigationStart |
| onload时间 | loadEventEnd - navigationStart |
2. performance.now()
performance.now()方法返回当前网页自从performance.timing.navigationStart到当前时间之间的毫秒数。
通过两次调用performance.now()方法,可以得到间隔的准确时间,用来衡量某种操作的耗时。
var start = performance.now();
doTasks();
var end = performance.now();
console.log('耗时:' + (end - start) + '毫秒。');
3. performance.mark()
mark方法用于为相应的视点做标记。clearMarks方法用于清除标记,如果不加参数,就表示清除所有标记。
indow.performance.mark('mark_fully_loaded');
window.peformance.clearMarks('mark_fully_loaded');
4. performance.getEntries()
浏览器获取网页时,会对网页中每一个对象(脚本文件、样式表、图片文件等等)发出一个HTTP请求。performance.getEntries方法以数组形式,返回这些请求的时间统计信息,有多少个请求,返回数组就会有多少个成员。
5. performance.navigation 对象
一些用户行为信息,主要都存放在performance.navigation对象上面。
performance.navigation.type
该属性返回一个整数值,表示网页的加载来源,可能有以下4种情况:
-
0:网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载,相当于常数performance.navigation.TYPE_NAVIGATENEXT。
-
1:网页通过“重新加载”按钮或者location.reload()方法加载,相当于常数performance.navigation.TYPE_RELOAD。
-
2:网页通过“前进”或“后退”按钮加载,相当于常数performance.navigation.TYPE_BACK_FORWARD。
-
255:任何其他来源的加载,相当于常数performance.navigation.TYPE_UNDEFINED。
performance.navigation.redirectCount
该属性表示当前网页经过了多少次重定向跳转。