天天看点

前端监控和页面卡顿

上一篇文章结尾提到了一个对图片(图片!不是图像!)至关重要的点:​​采样、量化和编解码​​​。

图像的显示需要GPU和CPU两者配合,CPU主要负责视图的创建,布局的计算和视图的绘制,然后进行图片的解码,将生成的位图交给GPU,GPU进行渲染,并将渲染的结果交到帧缓冲区,待下一个VSync 信号到来的时候视频控制器从帧缓冲区取出数据,经过转换,显示到屏幕上。

如果在规定的​​

​16.7ms​

​内,CPU和GPU的合作未完成,没有生成新的渲染数据到帧缓冲区中,那么就会出现卡顿或者掉帧的情况。

一般我们常说的“精灵图”Sprite 其实不止优化了“网络请求”,也利用了这一点,“尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示”。

除了上面提到的“图片加载”导致的页面卡顿。引起页面卡顿的场景还有很多。大致有两类:

  1. 渲染引起的卡顿
  2. 内存引起的运行卡顿

为了用户体验考虑,我们需要对这些现象进行上报,以便更快、有针对性的进行优化和修复。

其中第一种情况又有:“资源引起的页面掉帧”、“页面 FPS 持续低于预期”、“交互行为引起的渲染问题”。

卡顿严格来说还分为“正常卡顿”和“真实卡顿”(这里“真实”的意思是达到需要上报条件的)。而我们需要上报的就是“真实卡顿”。比如:

  • 在页面 FPS 连续​

    ​n 秒​

    ​内低于一个值时;
  • 当用户进行交互行为后,渲染新的一帧的时间超过​

    ​16ms + 100ms​

    ​时;
  • web worker心跳检测(针对页面崩溃!)

想想看,我们可以把“页面崩溃异常”也归为“页面卡顿”。这样的话就可以统一在某些行为不及预期时上报它们。

以上这些已经属于“错误上报”的范畴了。而我认为,一定要区分开错误上报和数据埋点(也叫埋点上报),这两者有本质区别和各自界定分明的作用场景。

页面崩溃这种行为通常是两种场景导致的:

  1. 过长时间占用JS线程(我们可以把它叫做“主线程”),比如无限循环,触发了浏览器的保护策略;
  2. 内存不足
这么看来页面崩溃就是一个“过火了”的“页面卡顿”。

在这两种场景中,毫无疑问主线程被阻塞,因此对崩溃的监控只能在独立于 JS 主线程的 Worker 线程中进行,我们可以采用 ​

​Web Worker 心跳检测​

​​的方式来对主线程进行不断的探测,如果主线程崩溃,就不会有任何响应,就直接进行崩溃异常的上报。

比如我们可以这么设置:

对JS 主线程:固定时间间隔向 ​​

​Web Worker​

​​ 发送心跳

对​​

​Web Worker​

​线程:

  • 每隔固定时间检查是否收到心跳(这个间隔和主线程发送心跳的间隔一致);
  • 超过一定时间(间隔时间+n秒)未收到心跳,则认为页面崩溃。
  • 检测到崩溃后,通过 http 请求进行异常上报。

上面的流程是以主线程优先。当然,你也可以换一种思路:

// 主线程
var worker = new Worker('worker.js');

// 父给子传一个初始时候的标志位
worker.postMessage(-1);
worker.onmessage = function(e) {
    //收到worker线程传来的消息
    console.log('Message received from worker', e.data);
    worker.postMessage(e.data);
}      
// 子线程
let date_begin = null;
let dTip = 0;
let newTip = 0;
let new_led = 0;
setInterval(()=> {
    if(!newTip && date_begin || !new_led) {
        // 被认为是崩溃
    } else {
        new_led = 0;
        postMessage(++dTip);
    }
},2000)
onmessage = function(e) {
    let d = Date.now();
    if(e.data < 0) {
        newTip = 1; //修改标志位确认符
        date_begin = d; //初始化时间
        return
    }
    if(d - date_begin > 4000) {
        // 被认为是崩溃
    } else {
        date_begin = d;
        new_led = 1;
    }
}      

继续阅读