https://zhuanlan.zhihu.com/p/39292837

https://zhuanlan.zhihu.com/p/40273861

要监控网页的卡顿,我们必须从 FPS 说起。

FPS 是来自视频或者游戏里的概念,即是每秒的帧数,代表视频或者游戏的流畅度,俗话说,就是“不卡”。

那在前端开发领域,网页的 FPS 是什么呢?

什么是网页的 FPS?

网页内容在不断变化之中,网页的 FPS 是只浏览器在渲染这些变化时的帧率。帧率越高,用户感觉网页越流畅,反之则会感觉卡顿。

在 Chrome 中可以通过开发者工具查看网页的 FPS:

注意,网页不是随时都需要,该工具看到的是每次更新是 FPS 值。

最优的帧率是 60,即16.5ms 左右渲染一次。

通过 Chrome 或者 Firfox 等的性能工具也可以查看浏览器的帧率:

绿色的直方图即代表在页面重新绘制时的帧率,Frames 为每一帧渲染所花的时间。

另外一种给出网页 FPS 的方式

FPS extension 是 Chrome 的一个扩展,可以显示当前网页的 FPS 值,即页面是否卡顿。

这种工具获取页面 FPS 的方式与浏览器自身给出的方式不同,没有采用浏览器原生的 API(正在制定),这类工具独辟蹊径,通过浏览器的 requestAnimationFrame API (可以使用 setInterval polyfill)来实现。

代码类似:

var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();
var loop = function(time) {var now =  performance.now();var fs = (now - lastFameTime);lastFameTime = now;var fps = Math.round(1000/fs);frame++;if (now > 1000 + lastTime) {var fps = Math.round( ( frame * 1000 ) / ( now - lastTime ) );frame = 0;    lastTime = now;    };           window.requestAnimationFrame(loop);
}

代码摘自淘宝前端团队的《无线性能优化:FPS 测试》。

通俗地解释就是,通过 requestAnimationFrame API 来定时执行一些 JS 代码,如果浏览器卡顿,无法很好地保证渲染的频率,1s 中 frame 无法达到 60 帧,即可间接地反映浏览器的渲染帧率。关于 requestAnimationFrame API 的细节,可以参看 MDN 上的文档。

如何监控网页的卡顿?

首先为什么要监控?比如在全民直播,我们必须重视用户在观看视频的体验,任何网页或者播放器的卡顿都会让用户抓狂。所以必须监控起来,以此来指导优化。

最后,回到本文的主题,如何监控网页的卡顿呢?

以全民直播的方式为例,使用 FPS extension 类似的方式,每秒中计算一次网页的 FPS 值,获得一列数据:

...6,8,11,29,60,58,46,57,57,57,44,59,51,54,0,31,58,56,41,52,51,17,22, 34,51,48,26,26,49,59,59,59,59,52,52,0,45,58,60,59,60,21...

然后通过通用的日志通道上报到大数据平台进行分析即可。

那如何通过 FPS 确定网页存在卡顿呢?按照我们对卡顿的观察,连续出现3个低于20的 FPS 即可认为网页存在卡顿。

function isBlocking(fpsList, below=20, last=3) {var count = 0for(var i = 0; i < fpsList.length; i++) {if (fpsList[i] && fpsList[i] < below) {count++;} else {count = 0}if (count >= last) {return true}}return false
}

当然这只是一种经验,但可以作为相对的度量。

通过这种方式,我们得到了网页卡顿的统计数据:

接下来,就可以针对卡顿的问题,在有数据支持的情况下进行优化我们的网页了!

溃和卡顿有何差别?

卡顿也就是网页暂时响应比较慢,JS 可能无法及时执行,这也是上篇网页卡顿监控所依赖的技术点。

但崩溃就不一样了,网页都崩溃了,页面看不见了,JS 都不运行了,还有什么办法可以监控网页的崩溃,并将网页崩溃上报呢?

但,天无绝人之路,方法总是有的。

load 与 beforeunload 事件

搜遍互联网,几乎找不到方法,最终碰上了这篇文章。本文利用 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控。

http://jasonjl.me/blog/2015/06/21/taking-action-on-browser-crashes/​jasonjl.me

  window.addEventListener('load', function () {sessionStorage.setItem('good_exit', 'pending');setInterval(function () {sessionStorage.setItem('time_before_crash', new Date().toString());}, 1000);});window.addEventListener('beforeunload', function () {sessionStorage.setItem('good_exit', 'true');});if(sessionStorage.getItem('good_exit') &&sessionStorage.getItem('good_exit') !== 'true') {/*insert crash logging code here*/alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));}

一图胜千言:

使用 load 和 beforeunload 事件实现崩溃监控

这个方案巧妙的利用了页面崩溃无法触发 beforeunload 事件来实现的。

在页面加载时(load 事件)在 sessionStorage 记录 good_exit 状态为 pending,如果用户正常退出(beforeunload 事件)状态改为 true,如果 crash 了,状态依然为 pending,在用户第2次访问网页的时候(第2个load事件),查看 good_exit 的状态,如果仍然是 pending 就是可以断定上次访问网页崩溃了!

但这个方案有问题:

  1. 采用 sessionStorage 存储状态,但通常网页崩溃/卡死后,用户会强制关闭网页或者索性重新打开浏览器,sessionStorage 存储但状态将不复存在;
  2. 如果将状态存储在 localStorage 甚至 Cookie 中,如果用户先后打开多个网页,但不关闭,good_exit 存储的一直都是 pending,完了,每有一次网页打开,就会有一个 crash 上报。

全民直播 一开始采用的就是这个方案,发现就算页面做了优化,crash 不下降,与 PV 保持比例,才意识到这个方案的问题之处。

基于 Service Worker 的崩溃统计方案

随着 PWA 概念的流行,大家对 Service Worker 也逐渐熟悉起来。基于以下原因,我们可以使用 Service Worker 来实现网页崩溃的监控:

  1. Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃;
  2. Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;
  3. 网页可以通过 navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。

基于以上几点,我们可以实现一种基于心跳检测的监控方案:

  • p1:网页加载后,通过 postMessage API 每 5s 给 sw 发送一个心跳,表示自己的在线,sw 将在线的网页登记下来,更新登记时间;
  • p2:网页在 beforeunload 时,通过 postMessage API 告知自己已经正常关闭,sw 将登记的网页清除;
  • p3:如果网页在运行的过程中 crash 了,sw 中的 running 状态将不会被清除,更新时间停留在奔溃前的最后一次心跳;
  • sw:Service Worker 每 10s 查看一遍登记中的网页,发现登记时间已经超出了一定时间(比如 15s)即可判定该网页 crash 了。

一些简化后的检测代码,给大家作为参考:

// 页面 JavaScript 代码
if (navigator.serviceWorker.controller !== null) {let HEARTBEAT_INTERVAL = 5 * 1000; // 每五秒发一次心跳let sessionId = uuid();let heartbeat = function () {navigator.serviceWorker.controller.postMessage({type: 'heartbeat',id: sessionId,data: {} // 附加信息,如果页面 crash,上报的附加数据});}window.addEventListener("beforeunload", function() {navigator.serviceWorker.controller.postMessage({type: 'unload',id: sessionId});});setInterval(heartbeat, HEARTBEAT_INTERVAL);heartbeat();
}
  • sessionId 本次页面会话的唯一 id;
  • postMessage 附带一些信息,用于上报 crash 需要的数据,比如当前页面的地址等等。
const CHECK_CRASH_INTERVAL = 10 * 1000; // 每 10s 检查一次
const CRASH_THRESHOLD = 15 * 1000; // 15s 超过15s没有心跳则认为已经 crash
const pages = {}
let timer
function checkCrash() {const now = Date.now()for (var id in pages) {let page = pages[id]if ((now - page.t) > CRASH_THRESHOLD) {// 上报 crashdelete pages[id]}}if (Object.keys(pages).length == 0) {clearInterval(timer)timer = null}
}worker.addEventListener('message', (e) => {const data = e.data;if (data.type === 'heartbeat') {pages[data.id] = {t: Date.now()}if (!timer) {timer = setInterval(function () {checkCrash()}, CHECK_CRASH_INTERVAL)}} else if (data.type === 'unload') {delete pages[data.id]}
})

都挺简单的代码,不细说了。

方案的可行性

兼容性:

Service Worker 的普及率已经相当高了,鉴于国内各种浏览器都是 Chrome 内核,而且版本已经在 Chrome 45 以上,已经覆盖了相当一部分用户。作为监控,数据覆盖大部分就好。

Service Worker 兼容性

可靠性:

这应该是我目前已知可以相对准确判断出网页崩溃的方式了。不过我们的方案还在测试环境,上线一段时间后再给大家共享数据。

对浏览器厂商的建议

题图的 Crash 列表,可以在 Chrome 中访问 chrome://crashes/ 看到,如果厂商可以提供一个 API,在页面打开时,可以获知用户上一次崩溃的信息就很棒了!

如何监控网页卡顿和崩溃?相关推荐

  1. 监控摄像头卡顿_监控画面卡顿的解决方法

    一般情况下,如果监控画面出现卡顿.动作缓慢的现象,其解决办法是先要搞清楚问题的根源,然后再从根本上解决问题.下面,我们就从监控故障最常见的几点入手,进行逐一排查. 1.监控数据线路接触问题. 我们首先 ...

  2. 优化 cesium 界面广告牌(billboard)数据量大于 10w +时,地图加载缓慢、卡顿、加载完成后浏览器严重卡顿甚至崩溃问题

    优化 cesium 界面广告牌(billboard)数据量大于 10w +时,地图加载缓慢.卡顿.加载完成后浏览器严重卡顿甚至崩溃问题 前言: 项目之前的设计,billboard 广告牌是绑在 ent ...

  3. 国产兼容三菱FX3U源码,最新一波bug修改,修改监控时卡顿 问题

    国产兼容三菱FX3U源码,最新一波bug修改,修改监控时卡顿 问题.8位口令功能,程序消除功能,定时器特殊情况下不运行问题.带modbus-tcp功能,实时时钟,深度测试后,修改一些主要bug后,稳定 ...

  4. 国产兼容三菱FX3U源码 ,修改监控时卡顿问题。 8位口令功能,程序消除功能

    国产兼容三菱FX3U源码,最新一波bug修改,修改监控时卡顿问题. 8位口令功能,程序消除功能,定时器特殊情况下不运行问题. 带modbus-tcp功能,实时时钟,深度测试后,修改一些主要bug后,稳 ...

  5. edge浏览器打开多个网页卡顿解决办法

    edge有时候打开了十几个页面就大量占据内存了,卡的不行,上网汇总了解决方法,具体参考以下两篇文章,一个是通过edge浏览器自身的设置修改,一个是关闭gpu相关的图形加速插件.按照以下两篇文章的方法基 ...

  6. 通过setTimeout来取消因大量计算造成的网页卡顿

    js是单线程的,所以有些大量计算的操作会占用线程资源,导致页面卡住. 今天遇到这样一个场景,选择一个下拉框之后,对数据进行筛选,这个过程中有大量计算,点了selecte的option之后,option ...

  7. 网页卡顿是网站服务器,想解决网站卡顿情况就用这几种诀窍,进来看看

    大家应当碰到过进网站的时候突然就很卡的情况,有的朋友方法都试遍了也就只能缓解一点点,网站涌现卡顿不仅影响到你自己,还会影响到客户的体验度,增长网站跳出率,这个问题让人很头疼了,那么我们应当怎么完整解决 ...

  8. Google浏览器打开网页卡顿怎么解决?

    今天用google的时候突然网页打开速度又变慢了-不是软件的问题 打开计算机的代理设置

  9. 服务器装了16g内存只显示4g,加装16G内存,电脑却运行卡顿、崩溃?原来是这项设置有问题!...

    也不知道是从什么时候开始,就流传出只要物理内存充足,那么虚拟内存就无需设置,可以直接关闭的说法. 而电脑店也有那么一位朋友,在加装了16G内存之后,关闭虚拟内存,想着终于不用担心电脑运行不够快了. 事 ...

  10. 如何利用 RunLoop 原理去监控卡顿?

    卡顿问题,就是在主线程上无法响应用户交互的问题.如果一个 App 时不时地就给你卡一下,有时还长时间无响应,这时你还愿意继续用它吗?所以说,卡顿问题对 App 的伤害是巨大的,也是我们必须要重点解决的 ...

最新文章

  1. 用jackson转json_用Jackson编写大JSON文件
  2. 关于在Ubuntu下安装配置numpy,scipy,matplotlibm,pandas 以及sklearn
  3. txtv28pw河南某中学_中学生骑行典型交通事故案例集 | 知危险会避险
  4. %3cphp和%3c php_phpcmsv9后台登录绕过
  5. 喝什么汤对肝脏有好处?
  6. 盘一盘新冠病毒下,国内外科技公司提出哪些软件解决方案与技术?
  7. 概率软逻辑(PSL,Probabilistic soft logic)通用(可处理中文)版本
  8. 美化博客园 添加网易云音乐及生成文章目录
  9. Share:思科模拟器 路由配置(默认、静态、动态RIP)
  10. MCSA / Windows Server 2016 PowerShell DSC
  11. AE快速输出GIF动图格式插件:GifGun for Mac(支持ae2022)
  12. js里的一些even事件
  13. 索引及其背后的数据结构(顺带介绍了一下子查询和合并查询)
  14. Android获取内置sdcard跟外置sdcard路径
  15. 软件测试永远的家——银行测试,YYDS
  16. 分布式存储系统——HBase
  17. Gretna网络分析之先验知识
  18. Unity提供的消息推送机制
  19. C++ 检查内存泄露工具
  20. 多种经典色差计算公式——matlab代码

热门文章

  1. 基于MiniC的语义分析(使用javacc)
  2. 矩阵键盘的原理及应用
  3. uniapp windows 真机调试 ios iphone 踩坑指南 itunes 64位历史版本
  4. [运算放大器系列]二、电压转4 - 20MA电流电路分析
  5. VS1005 功放板
  6. js高级开发:气泡坦克(js对象,属性)
  7. 医院计算机房相关制度,医院机房管理制度.docx
  8. PHP连接MySQL-踩坑总结
  9. Win10 安装 Rational Rose 教程(带超详细图文)
  10. windows杀进程