先确认性能指标。例如 FCP、FP、FPS、平均请求时间等。
这些性能监控的结果,能够展现前端性能的好坏,根据性能监测的结果能够进一步的去优化前端性能,尽量的提升用户体验。
性能指标的计算
FPS 与 卡顿
FPS(Frames Per Second):每秒显示帧数。一般 FPS 在 60 以上,页面流畅,不卡顿。
计算 FPS 的思路:
关键:算出一帧 占 多少毫秒
宏任务——> 微任务——> requestAnimationFrame ——> 渲染 | ——> 宏任务 ——>
假设页面加载用时 X ms,这期间 requestAnimationFrame 执行了 N 次, 则帧率为 1000 * N/X,也就是 FPS
/**
* 不同的客户端有差异,考虑一下 requestAnimationFrame 的兼容性
* 在不支持 requestAnimationFrame 时,使用 setTimeout 来模拟实现
*/
var requestAnimationFrameCompatibility = (function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60)
}
)
})()
var fpsConfig = {
lastFrameTime: performance.now(), // 记录上次统计帧数的时间
count: 0 // x ms 内,执行了 count 次 requestAnimationFrame
}
// 计算 FPS 的值
var fpsLoop = function () {
var now = performance.now()
fpsConfig.count += 1
if (now > (1000 + fpsConfig.lastFrameTime)) {
var fps = Math.round((1000 * fpsConfig.count) / (now - fpsConfig.lastFrameTime))
console.log(`FPS 值为 ${fps}`);
fpsConfig.lastFrameTime = now
fpsConfig.count = 0 // 重置 count
}
requestAnimationFrameCompatibility(fpsLoop)
}
fpsLoop()
卡顿不能单纯用单次的 FPS 来衡量:FPS 低于 60 并不意味着卡顿,那 FPS 高于 60 也非一定不卡顿。 比如前 60 帧渲染很快(10ms 渲染 1 帧),后面的 3 帧渲染很慢( 20ms 渲染 1 帧),这样平均起来 FPS 为95,高于 60 的标准。这种情况会不会卡顿呢?明显是卡顿的。
我们用多次的 FPS 来衡量卡顿情况:如果有 3 次连续 FPS < 20,则说明卡顿。
FPS < 20,意味着每秒渲染 20 帧,卡到爆了。
/**
* 判断是否卡顿
* @param {Array<number>} fpsList 记录了 FPS 的数组
* @param {Number} below 低于某个值
* @param {Number} last 持续几次
*/
function isBlocking(fpsList, below, last) {
var count = 0
for (let i = 0; i < fpsList.length; i += 1) {
if (fpsList[i] && fpsList[i] < below) count += 1
else count = 0
if(count > last) return true
}
return false
}
FP 与 白屏
浏览器的页面加载过程:客户端发起请求 -> 下载 HTML 及 JS/CSS 资源 -> 解析 JS 执行 -> JS 请求数据 -> 客户端解析 DOM 并渲染 -> 下载渲染图片-> 完成整体渲染。
客户端解析 DOM 并渲染之前的时间就是白屏时间
navigationStart 和 fetchStart 的区别: navigationStart 是在上一个文档卸载之后,开始计算的。(即切换 tab 栏) fetchStart 是在发起请求时开始计算的。 如果没有上一个文档,navigationStart = fetchStart
// 白屏时间 = 页面开始展示时间点 - 开始请求时间点
const FP = performance.timing.domLoading - performance.timing.navigationStart
App 的页面加载过程:初始化 WebView -> 客户端发起请求 -> 下载 HTML 及 JS/CSS 资源 -> 解析 JS 执行 -> JS 请求数据 -> 服务端处理并返回数据 -> 客户端解析 DOM 并渲染 -> 下载渲染图片 -> 完成整体渲染。
App 下的白屏时间,多了启动浏览器内核,也就是 Webview 初始化的时间。在 App 测试版本中,程序在 App 创建 WebView 时打一个点,然后在开始建立网络连接打一个点,这两个点的时间差就是 Webview 初始化的时间。
FCP 与 首屏
首次内容绘制(首屏)FCP(First Contentful Paint)
:这个指标用于记录页面首次绘制文本、图片、非空白 Canvas 或 SVG 的时间。
new Promise((resolve, reject) => {
new PerformanceObserver((list) => {
resolve(list);
}).observe({ entryTypes: ["paint"] })
}).then((list) => {
list.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.startTime}`);
})
}).catch((error) => {
console.warn(error);
})