h5白屏异常监控技术调研

3,265 阅读4分钟

前言

但凡存在类似“自动搭建”的功能页、活动页、CMS页面都不可避免遇到一类问题:秒开率较之其它页面差距较大

秒开率这个性能指标是最直观的性能观测指标之一,也是公司老板们肯定你的依据之一。现在的前端越来越卷了,你的老板可能每天都会针对某些秒开率低下的页面来质询你。

那么,页面白屏可能会带来什么?

白屏带来的不确定性

1. 请求异常

我们知道浏览器发出http请求,会包含 headers、 method、contentType等,服务器收到请求还要有相应的response。 请问,到底是 request 出了问题,还是 response 出了问题? 下面是随便一个请求的request header,瞅瞅。

image.png

如果此时最关键且决定视图渲染的 js 脚本没正常获取到,会咋样? 很明显,这白屏不过吧?

2. 意外的异常

先说 try catch

    try {
        const a = 0;
        a.push(1);
    } catch (error) {
        console.error(error);
        console.log('I m comming!')
    }

结果很明显

image.png

被 catch 住的异常只能说是 “正常的异常”。 能被捕捉到的异常前端自己处理就行了,根本不用BB。

so. Q1: 如果异常没被步骤到呢?

// 假如强行丢出错误呢?
throw 'a.push is not a function....'

// 假如就是没有捕捉错误呢?
 const a = 0;
 a.push(1);
 console.log('I m comming now!')

还会像catch那样,最后会 打印出 'I m comming now!'吗? 明显不会,因为你的js代码已经被停止向后执行了。

所以,你白屏很正常吧?

Q2: 虽然你说得都对,那你只要写代码的时候细心些不就行了吗?

答: 不好意思。虽然你说的我都懂,但就是有些页面是运营搭起来的,不是开发每次手写的。

小结一下

要知道白屏时间里到底发生了什么,得至少监控到以下几点:

  • 监控 request
  • 监控 response
  • 监控 V8 告诉你的异常。例如js代码执行报错Error
  • 控制台console
  • 加载资源时间

puppeteer

简介不多说了,各位大佬去看官网,我就不浪费时间了。直接说,它能做到哪些事情?

1. 无头访问页面

async function main() {
    // 启动chrome浏览器
    const browser = await puppeteer.launch({
        // 指定该浏览器的路径,我mac没有指定😂
        // executablePath: chromiumPath,
        // 是否为无头浏览器模式,默认为无头浏览器模式
        headless: true
    });

    // 在一个默认的浏览器上下文中被创建一个新页面
    const page1 = await browser.newPage();
    const eventTypes = ['domcontentloaded', 'error', 'frameattached', 'framedetached', 'framenavigated', 'load', 'metrics', 'pageerror', 'popup', 'request', 'response', 'requestfailed', 'requestfinished', 'requestservedfromcache', 'workercreated', 'workerdestroyed']

    // 空白页刚问该指定网址
    await page1.goto('https://blog.csdn.net/weixin_31742871/article/details/112741137');
}

然后你会发现你没有打开浏览器,却访问到了页面。 无头浏览器由此得来。

2. 截取DOM元素的能力

async function myCheck() {
    const browser = await puppeteer.launch({
        headless: false
    });

    const page = await browser.newPage();

    await page.goto('https://juejin.cn/user/2084329775958248/posts');

    await page.waitForSelector('body'); // 异步等待<body />出现

    // 用page自带的方法获取节点
    const children = await page1.$eval('body', el => el.children);
    
    console.log(children);// 是渣男吧 的个人主页 - 文章

}

myCheck();

如果拿到一些关键DOM,例如首屏最后一个DOM元素,是不是就可以计算时间了?

3. 监听请求/响应 & 拦截请求

async function myCheck() {
    // same code ....
    // 拦截请求开启 
    
    await page.setRequestInterception(true);// true开启,false关闭
    
    page.on('request', request => {
        if (request.url() === 'https://mcs.snssdk.com/v1/list') {
            request.abort(); 
            console.log('该请求被终止!!!');
           
        }else {
            console.log(request.resourceType());
            console.log(request.method());
            console.log(request.headers());
        }
        
    });

    page.on('response', response => {
        if (response.url() === 'https://mcs.snssdk.com/v1/list') {
            console.log(response.status());
            console.log(response.headers());
        }
    })

    // 空白页刚问该指定网址
    await page1.goto('https://www.baidu.com');
}

myCheck();

不多说,代码懂的都懂。不懂的请留言,单独解惑。

4. snapshot 截图/快照/PDF

此功能吹爆。

async function screenshot() {

    await page.screenshot({
        path: 'Images/screenshot.png', // 保存路径
        fullPage: true // 全屏截取
    });

    // 截取屏幕中一个区域的内容
    await page.screenshot({
        path: 'Images/screenshot.jpg',
        type: 'png',
        quality: 80,
        clip: {
            x: 0,
            y: 0,
            width: 375, // 屏幕宽
            height: 600
        }
    });
    
    await page.pdf({ path: 'Pdf/errorInfo.pdf' }); // 生成PDF

    browser.close();
}
screenshot();

so,当你监控到异常发生,不管三七二十一先把当前的样子截个图保存好。老板回头就把证据丢你脸上😂

最后看下有哪些可以被监听的事件

上源码:

/**
 * Denotes the objects received by callback functions for page events.
 *
 * See {@link PageEmittedEvents} for more detail on the events and when they are
 * emitted.
 * @public
 */
export declare interface PageEventObject {
    close: never;
    console: ConsoleMessage;
    dialog: Dialog;
    domcontentloaded: never;
    error: Error;
    frameattached: Frame;
    framedetached: Frame;
    framenavigated: Frame;
    load: never;
    metrics: {
        title: string;
        metrics: Metrics;
    };
    pageerror: Error;
    popup: Page;
    request: HTTPRequest;
    response: HTTPResponse;
    requestfailed: HTTPRequest;
    requestfinished: HTTPRequest; 
    requestservedfromcache: HTTPRequest;
    workercreated: WebWorker;
    workerdestroyed: WebWorker;
}

end

其实,所谓技术调研,就是你要做某样事情(可能是老板让你做)之前,你要确认一下存在可行性方案的可能。

做到这几步:

  • 技术栈的稳定性、成熟性。
  • 能不能做。
  • 能做的话大概会遇见什么难踩的坑。
  • 兼容性问题
  • 人力成本如何
  • 用户群
  • 是否考虑并发

以上搞定了,确定了要做,那你就去和老板怼(沟通)吧。