前端上报页面崩溃

536 阅读3分钟

背景

对于我们开发者而言,如果访客登录了我们网站,但是登录之后整个页面崩溃了,这不仅让访客失去对页面的信任,也造成了公司经济流失,对此我总结了以下浏览器崩溃的原因:

  • 内存泄漏。这是我在一次研发three项目时候遇到的,three显示三维场景需要初始化场景、摄像机还有渲染器,不仅如此,想要看到物体还得添加模型,如果模型多而且复杂多样,就会增加内存负担,而当我们退出三维场景页面时候,three是不会自主进行模型内存回收的,这也意味着你每访问一次,内存就会堆叠,频繁访问之后就会崩溃。 image.png

  • 项目内部存在无法跳出的循环。如果我们常用while语句就更需要注意了,无限循环的js代码直接占用浏览器主线程,导致浏览器崩溃。(这我就不放出图片了,尽管关闭就没事了,但仍然觉得膈应)

  • 项目代码复杂多样。如果一个网站打开需要进行大量循环,并且渲染的数据非常多,就会非常卡顿,或者是网站代码质量低,占用了许多内存,如果这时候再遇上一些比较老旧的浏览器,就雪上加霜了。

    那么我们该如何监控到浏览器崩溃,及时换回损失呢?浏览器崩溃,那也就意味着主线程无法运行,在主线程添加监控代码也就不现实了,那么我们该如何脱离主线程监控到崩溃呢?


基于service worker的心跳检测

service worker在浏览器中是有自己独立的线程的,浏览器崩溃后,service worker所在的线程是不受影响的,并且它与主线程是可以通过postMessage发送事件与service worker建立联系。worker我相信大家都见过,但service worker是跟普通的worker是有区别的,用new Worker传入的js文件会在当前页面标签关闭后结束,而使用service worker只会在浏览器关闭后才结束执行,对于一些客户,页面崩溃也只会关闭当前标签,所以service woker生命周期更长,可以更好的检测页面状态。 基本思想如下:

未命名绘图.drawio.png

  • 用户打开网页后,主线程开启定时器,每隔5s给service worker发送一次信号。主线程能够发送信号是可以证明主线程还在正常运行,没有出现页面崩溃。
  • service worker也开启定时器,每隔7s(至少比5s长)向后端发送崩溃信号,并且根据主线程发来的心跳信号,更新定时器,避免发送错误消息。 页面开始之后会发生两种情况,页面崩溃或者用户正常关闭。
  • 页面崩溃之后主线程不再发送心跳信号,那么service worker线程内的定时器得不到更新,就会在时间结束后发送日志给后端,上报崩溃情况。
  • 页面正常关闭需要我们发送页面关闭信号来关闭service worker线程的定时器,防止发送错误消息。 整体代码如下所示:

主线程:

//监控页面崩溃
export function init_track_crash() {
    if ('serviceWorker' in navigator) {

        navigator.serviceWorker.register('../work.js');
        let heartbeat = function () {
            navigator.serviceWorker.controller.postMessage(true)
        }
        heartbeat()
        setInterval(heartbeat, 3000);
        window.addEventListener('unload',()=>{
            navigator.serviceWorker.controller.postMessage(false)
        })
    }
}

service worker线程:


let check_timer
onmessage = (e) => {
    const { data } = e
    if (data) {
        if (check_timer) clearTimeout(check_timer)
        check_timer = setTimeout(() => {
            fetch("http://localhost:80/performance/crash", {
                method: "POST",
                body: JSON.stringify({
                    kind: "performance",
                    type: "crash",
                    startTime: Date.now()
                }),
                headers: {
                    'Content-Type': 'application/json'
                    // 'Content-Type': 'application/x-www-form-urlencoded',
                }
            })
        }, 5000);
    }else{
        if (check_timer) clearTimeout(check_timer)
    }

}

注意点:

  • service worker是浏览器关闭后才消失的,所以每次测试最好关闭浏览器,不然浏览器内部还是原来的文件。
  • 如果你是Vue项目,你的service worker文件需要放到这里,并引用本地路径获取,如下:

95b619207a407dcfb50e1b5eef3b4a8.png