diff --git a/README.md b/README.md index aeb2729a1..cfd1f3951 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ 如果你访问不了,可以[来这里看](http://www.godbasin.com)哦~ ## 最近更新 - + +- [《前端性能优化--用户卡顿检测》](https://godbasin.github.io/front-end-playground/front-end-basic/performance/front-end-performance-jank-detect.html) - [《让你的长任务在 50 毫秒内结束》](https://godbasin.github.io/front-end-playground/front-end-basic/performance/front-end-performance-long-task.html) - [《前端性能优化--数据指标体系》](https://godbasin.github.io/front-end-playground/front-end-basic/performance/front-end-performance-metric.html) - [《有趣的 PerformanceObserver》](https://godbasin.github.io/front-end-playground/front-end-basic/performance/front-end-performance-about-performanceobserver.html) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 75f102053..d3c4a00c2 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -51,6 +51,7 @@ module.exports = { title: '前端性能优化', collapsable: true, children: [ + '/front-end-basic/performance/front-end-performance-jank-detect.md', '/front-end-basic/performance/front-end-performance-long-task.md', '/front-end-basic/performance/front-end-performance-optimization.md', '/front-end-basic/performance/front-end-performance-startup.md', diff --git a/docs/README.md b/docs/README.md index d87e2b655..e46f0b6a3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,7 @@ footer: MIT Licensed | Copyright © 2021-present 被删 ## 最近更新 +[《前端性能优化--用户卡顿检测》](./front-end-basic/performance/front-end-performance-jank-detect.md) [《让你的长任务在 50 毫秒内结束》](./front-end-basic/performance/front-end-performance-long-task.md) [《前端性能优化--数据指标体系》](./front-end-basic/performance/front-end-performance-metric.md) [《有趣的 PerformanceObserver》](./front-end-basic/performance/front-end-performance-about-performanceobserver.md) diff --git a/docs/front-end-basic/performance/front-end-performance-jank-detect.md b/docs/front-end-basic/performance/front-end-performance-jank-detect.md new file mode 100644 index 000000000..04a58615b --- /dev/null +++ b/docs/front-end-basic/performance/front-end-performance-jank-detect.md @@ -0,0 +1,101 @@ +--- +title: 前端性能优化--用户卡顿检测 +--- + +前面跟大家介绍过[前端性能卡顿的检测和监控](./front-end-performance-no-response-solution.md),其中提到了`requestAnimationFrame`心跳检测等方式来检测代码执行耗时,从而判断是否存在卡顿。 + +而实际上我们观察一些用户反馈,会发现这样检测的效果并不是很理想。 + +# 用户感觉的“卡” + +一般来说,我们会根据代码检测的任务耗时超过一定值判断为卡顿,比如超过 1s 的长任务。但实际上,这样的方法难以准确命中“用户侧卡顿”的场景,这是因为: + +- 超过 1s 的任务执行时,用户未必在进行页面操作,未感受到“卡顿” +- 对用户来说,在浏览器中各个过程中的卡顿阈值是不一致的,比如: + - 页面打开过程中,会习惯性地等待,此时卡顿阈值会稍微高一些 + - 页面加载完成后,对各种功能的操作响应更敏感,希望能快速响应操作 + +因此,我们可以重新定义卡顿指标,可以将其分为两种: + +1. 技术侧卡顿(代码长任务)。 +2. 用户侧卡顿(交互响应耗时)。 + +本文我们重点来探讨用户侧卡顿的检测。 + +## 用户侧卡顿 + +如果你有认真整理用户反馈,便会发现,对于大型应用比如在线表格/网页游戏等,相比于加载过程中偶尔一两秒的卡顿,更让他们难以接受的问题有频繁出现卡顿、某个操作卡顿耗时过长、某个较频繁的操作必现卡顿等。 + +那么,我们可以基于这些场景,重新定义用户侧卡顿的指标,满足以下场景均可认为产生了卡顿: + +| 问题 | 对应性能指标 | 指标定义 | 补充说明 | +| ------------------------------------------ | ----------------------------------------- | ------------ | ----------------------------------------------------------------- | +| 操作后响应不及时 | 用户交互(点击)后,rAF 响应耗时 > 1000ms | 交互卡顿 | 类似 INP(参考 https://web.dev/articles/inp),但滚动行为考虑在内 | +| 操作(编辑/滚动)频繁出现卡顿 | 20s 内,交互响应卡顿次数 > 5 | 交互卡顿频率 | | +| 某个操作卡顿耗时过长,长达 5s/10s 甚至更多 | - 交互响应卡顿耗时 > 5s | +| - 交互响应卡顿耗时 > 10s | 交互长耗时卡顿 | | +| 某个较频繁的操作必现卡顿 | 相同的卡顿埋点次数 > 5 | 同因交互卡顿 | | + +这里有一个难处理的地方:如何判断用户交互后产生了卡顿呢?因为我们可以拆分成以下情况: + +1. 用户交互后,同步执行长耗时任务产生卡顿。 +2. 用户交互后,异步执行逻辑的时候产生卡顿。 + +### 1. 同步任务卡顿 + +我们可以在监听到用户交互时进行耗时计算: + +```javascript +window.addEventListener("click", () => { + const startTime = new Date().getTime(); + requestAnimationFrame(() => { + const duringTime = new Date().getTime() - startTime; + // 交互后超过 1s 才响应 + if (duringTime > 1000) { + // 则判断为卡顿 + } + }, 0); +}); +``` + +### 2. 异步任务卡顿 + +对于异步任务,由于卡顿发生在用户交互后,难以通过代码直接发现。我们可以从另外一个角度分析,即当页面交互发生卡顿时,用户常常会在页面中进行操作,来确认页面是否无响应。因此,我们可以通过这样的代码判断: + +```javascript +let clickCount = 0; +let hasClick = false; +window.addEventListener("click", () => { + clickCount++; + if (hasClick) return; + hasClick = true; + setTimeout(() => { + // 卡顿过程中发生了连续点击操作 + if (clickCount > 2) { + // 则判断为卡顿 + } + // 清空数据 + clickCount = 0; + hasClick = false; + }, 0); +}); +``` + +## 总卡顿指标设计 + +综上所述,我们会将以下情况作为一次卡顿的产生,并且做卡顿次数的上报: + +- 用户交互后,同步卡顿超过 1s +- 检测到一次宏任务中,用户连续点击操作超过 5 次 + +同时,我们可以在特特定场景发生的时候,将数据以及日志同时进行上报,比如: + +- 20s 内产生卡顿次数 > 5 +- 检测到某段代码执行超过 5s/10s +- 检测到卡顿埋点中卡顿(超过 1s)的相同埋点多次产生(相同的卡顿埋点次数 > 5) + +通过这样的方式,我们可以判断用户是否产生了卡顿。但实际上要如何定位卡顿的位置呢,还是得通过日志和埋点进行,可以参考[《前端性能优化--卡顿的监控和定位》](./front-end-performance-no-response-solution.md)一文。 + +# 结束语 + +很多时候,我们开发在实现功能的时候,常常会从编程出发去思考问题,但实际上我们可以更贴近用户一些滴~