性能 - Web 网站和应用的支柱
寻找性能瓶颈
- 了解性能指标 - 多快才算快
- 利用测量工具和 APIs
- 优化问题,重新测量(迭代)
移动端挑战多
- 设备硬件、网络、屏幕尺寸、交互方式
- 用户更缺少耐心,大于 3 秒导致 53% 的跳出率(bounce rate)
- 持续增长的移动用户和移动电商业务
-
First Contentful Paint(FCP)
-
Speed Index
-
Largest Contentful Paint(LCP)
-
Time to Interactive
-
Total Blocking Time
-
Cumulative Layout Shift
Ctrl + Shift + P
- Frames Per Second (FPS)
-
理解加载瀑布图
-
基于 HAR 存储于重建性能信息
-
速度指数(Speed Index)
-
重要测量指标
-
Speed Index
-
TTFB
-
页面加载时间
-
首次渲染时间
- 交互动作的反馈时间
- 帧率 FPS
- 异步请求的完成时间
用户对于网站延迟的反应
- 0-16ms:动画的一帧渲染不大于 16ms 时,用户会感觉动画是流畅的。
- 0-100ms:当用户进行操作时,100ms 内窗口响应用户操作,用户会有一种立即获得结果的感觉。
- 100-300ms:用户可能微微察觉到一点延迟。
- 300-1000ms:此时长范围是用户能接受的页面的加载或者视图的改变所用时长。
- 1000+ms:用户的注意力将离开正在执行的任务。
- 10000+ms:用户已经没有耐心等待下去。
什么是 RAIL 模型
RAIL 是一种以用户为中心的性能模型,又 google 提出,其主要目标是让用户满意,主要分为 response animation idle load 四个部分
-
R 代表:Response,即”响应“的意思。一般指的是用户输入网页快速给出结果的过程,比如点击按钮,切换表单,启动动画之类的。
-
A 代表:Animation,即”动画“的意思。指的是涉及 style,layout,paint,composite 过程的行为。比如:用户滚动页面,拖动刷新,页面动画等。
-
I 代表:Idle,即”空闲“的意思。一般指用户看到网页到他们进行操作这一无交互时间段。
-
L 代表:Load,即”加载“的意思。一般指网页加载这一段时间
-
response 响应:输入延迟时间小于 100 毫秒,超过这个时间用户能感知到延迟
-
animation 动画:每一帧完成时间小于 16 毫秒,因为人眼可以感知到每秒渲染渲染 60 帧的平滑动画,也即每帧约 16 毫秒
-
idle 空闲:为了实现响应时间小于 100 毫秒,工作得分成不大于 50 毫秒的块,在 50 毫秒内将控制还给主线程
-
load:页面在 1000 毫秒内就绪
- R(响应)<100ms。 即,当用户点击按钮后,
尽量
(可以理解有些操作实在很难做到 100ms 内响应)在 100ms 的时间内要响应用户(应该在50ms 以内完成
)。一般对于="">500ms 才能完成的操作,最好给用户一些提示。 - A(动画)<16ms/帧。但由于浏览器绘制新帧到屏幕上需要时间,所以其实真正的每帧动画能用的时间只有 10ms 。(
每10ms产生一帧
) - I(空闲)<50ms。一般空闲时间是有 100ms,这段时间可以用来完成加载网页时非必需的加载,但是必须在 50ms 内将控制返回给主线程。(
尽可能增加空前时间
) - L(加载)<1000ms。即,在 1s 内加载网页,无需加载所有,在满足用户需求下只需加载关键资源,从而使用户获得较好的网络体验。(
在 5s 内完成内容加载并可以交互
)
- Chrome DevTools 开发调试、性能评测
- LightHouse 网站整体质量评估
- WebPageTest 多测试地点、全面性能报告
WebpageTest 主要提供了 Advanced Testing、simple Testing、Visual Comparison、Traceroute 四个功能
- 填写需要测试的 URL
- 填写 Test Location,下拉选择即可,可选移动端设备(包括 Android、IOS),可选 PC 端(分地区,每个地区可支持的浏览器不同)
- 可选支持的浏览器
- 点击右侧 START TEST
剩下的就是工具自己的事情了,只需要坐等分析结果。
- waterfall chart 请求瀑布图
- first view 首次访问
- repeat view 二次访问
参考附 1
First Byte Time
- 适用对象:访问页面的第一字节时间(后端处理+重定向)
- 检查内容:目标时间包括 DNS 寻址时间+建立连接时间(Socket) + SSL 认证时间 + 100ms。当超过目标时间每 100ms 时, 性能评定将降低一个等级
Keep-Alive
- 适用对象:同一个域名下多个页面对象使用了同一个连接(Socket)
- 检查内容:响应头文件包含"Keep-Alive"的指令或者在给定的主机中多个对象中使用同一个连接
GZIP Text
- 适用对象:工具会将 MIME 类型为"text/*" 或"*java*"的所有对象
- 检查内容:检查 Transfer-encoding 来看是否为 GZIP,如果不是则结果中会提供说明该文件是压缩过以及提供压缩比率(因此一个文件可以节省 30%的大小,通过压缩即产生了源文件 70%大小的文件)
Compress Images
- 适用对象:JPEG 图片
- 检查内容:对比使用 photoshop 质量选择为 50 后的文件大小,尺寸超出 10%为达标,10%~50%为警告,超出 50%为不达标,总体评分为图片重压缩后占原文件的百分比
Use Progressive JPEGs
- 适用对象:所有 JPEG 图片
- 检查内容:检查每个 JPEG 图片文件并计算分数,分数为图片的压缩比(压缩文件大小/原文件大小)
Cache Static
- 适用对象: 符合以下的情况的任意的非 html 对象数据,这个工具会将 MIME 类型为"text/","java"或者"image/",此类没有明确标明过期时间(0 或者-1),cache-controlheader 设置为 private,non-store 或者 non-cachepragma header 设置为 no-cache
- 检查内容:存在一个”Expires“ header(而不是 0 或者-1),或者设置 cache-control: max-age 并设置为一个小时或超过一个小时。当过期时间设置小于 30 天,将评定为警告
Use A CDN
- 适用对象:所有静态的非 HTML 内容(css, js 以及图片)
- 检查内容:检查是否托管在一个已知的 CDN 上(CName 映射到一个已知的 CDN 网络上).超过整体页面 80%为静态资源时,则需要考虑使用 CDN,将静态资源托管在 CDN 上,你可以从这里知道当前已知的 CDN
安装及使用
- 本地 npm 安装 LightHouse
- Chrome Devtools 中使用
- 通过 chrome web store 安装插件
LightHouse git:https://github.com/GoogleChrome/lighthouse
lighthouse 依赖 node 8 或者更高的 node 版本
npm install -g lighthouse
lighthouse https://www.baidu.com/
- LightHouse
- Throttling 调整网络吞吐
- Performance 性能分析
- Network 网络加载分析
其他常用功能
- Animations
- Request blocking
- Rendering
- Performance monitor
- 关键时间节点(Navigation Timing,Resource Timing )
- 网络状态(Network APIs)
- 客户端服务端协商(HTTP Client Hints) && 网页显示状态(UI APIs)
计算方式:
- DNS 解析耗时: domainLookupEnd - domainLookupStart
- TCP 连接耗时: connectEnd - connectStart
- SSL 安全连接耗时: connectEnd - secureConnectionStart
- 网络请求耗时 (TTFB): responseStart - requestStart
- 数据传输耗时: responseEnd - responseStart
- DOM 解析耗时: domInteractive - responseEnd
- 资源加载耗时: loadEventStart - domContentLoadedEventEnd
- First Byte 时间: responseStart - domainLookupStart
- 白屏时间: responseEnd - fetchStart
- 首次可交互时间: domInteractive - fetchStart
- DOM Ready 时间: domContentLoadEventEnd - fetchStart
- 页面完全加载时间: loadEventStart - fetchStart
- http 头部大小: transferSize - encodedBodySize
- 重定向次数:performance.navigation.redirectCount
- 重定向耗时: redirectEnd - redirectStart
// 计算一些关键的性能指标
window.addEventListener('load', (event) => {
// Time to Interactive 可交互时间
let timing = performance.getEntriesByType('navigation')[0];
console.log(timing.domInteractive);
console.log(timing.fetchStart);
let diff = timing.domInteractive - timing.fetchStart;
console.log('TTI: ' + diff);
});
// 观察长任务
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry);
}
});
// 监听 long tasks
observer.observe({entryTypes: ['longtask']});
// 见面可见性的状态监听
let vEvent = 'visibilitychange';
if (document.webkitHidden != undefined) {
// webkit prefix detected
vEvent = 'webkitvisibilitychange';
}
function visibilityChanged() {
if (document.hidden || document.webkitHidden) {
// 页面不可见
console.log('Web page is hidden.');
} else {
// 页面可见
console.log('Web page is visible.');
}
}
document.addEventListener(vEvent, visibilityChanged, false);
// 监听网络状态变化
var connection =
navigator.connection || navigator.mozConnection || navigator.webkitConnection;
var type = connection.effectiveType;
function updateConnectionStatus() {
console.log(
'Connection type changed from ' + type + ' to ' + connection.effectiveType
);
type = connection.effectiveType;
}
connection.addEventListener('change', updateConnectionStatus);