先确认性能指标。例如 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 += 1if (now > (1000 + fpsConfig.lastFrameTime)) {var fps = Math.round((1000 * fpsConfig.count) / (now - fpsConfig.lastFrameTime))console.log(`FPS 值为 ${fps}`);fpsConfig.lastFrameTime = nowfpsConfig.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 = 0for (let i = 0; i < fpsList.length; i += 1) {if (fpsList[i] && fpsList[i] < below) count += 1else count = 0if(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);})
