前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端性能优化--卡顿定位方案

前端性能优化--卡顿定位方案

原创
作者头像
被删
发布2024-01-27 09:43:24
3020
发布2024-01-27 09:43:24
举报

接上篇《卡顿的监控方案》,我们来介绍一下监控到卡顿之后,要怎么进行定位。

卡顿埋点上报

不管是哪种卡顿监控方式,我们使用检测卡顿的方案发现了卡顿之后,需要将卡顿进行上报才能及时发现问题。但如果我们仅仅上报了卡顿的发生,是不足以定位和解决问题的。

卡顿打点

那么,我们可以通过打点的方式来大概获取卡顿发生的位置。

举个例子,假设我们一个网页中,关键的点和容易产生长耗时的操作包括:

  1. 加载数据。
  2. 计算。
  3. 渲染。
  4. 批量操作。
  5. 数据提交。

那么,我们可以在这些操作的地方进行打点。假设我们卡顿工具的能力主要有两个:

代码语言:js
复制
interface IJank {
    _jankLogs: Array<IJankLogInfo & { logTime: number }>;
    // 打点
    log(jankLogInfo: IJankLogInfo): void;
    // 心跳
    _heartbeat(): void;
};

那么,当我们在页面加载的时候分别进行打点,我们的堆栈可能是这样的:

代码语言:js
复制
_jankLogs = [{
    module: '数据层',
    action: '加载数据',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '计算',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '渲染',
    logTime: xxxxx,
}, {
    module: '数据层',
    action: '批量操作计算',
    logTime: xxxxx,
}, {
    module: '数据层',
    action: '数据提交',
    logTime: xxxxx,
}]

当卡顿心跳发现卡顿产生时,我们可以拿到堆栈的数据,比如当用户在批量操作之后发生卡顿,假设此时我们拿到堆栈:

代码语言:js
复制
_jankLogs = [{
    module: '数据层',
    action: '加载数据',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '计算',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '渲染',
    logTime: xxxxx,
}, {
    module: '数据层',
    action: '批量操作计算',
    logTime: xxxxx,
}]

这意味着卡顿发生时,最后一次操作是数据层--批量操作计算,则我们可以认为是该操作产生了卡顿。

我们可以将module/action以及具体的卡顿耗时一起上报,这样就方便我们监控用户的大盘卡顿数据了,也较容易地定位到具体卡顿产生的位置。

心跳打点

当然,上述方案如果能达到最优效果,则我们需要在代码中关键的位置进行打点,常见的比如数据加载、计算、事件触发、JavaScript 加载等。

我们可以将打点方法做成装饰器,自动给class中的方法进行打点。如果埋点数据过少,可能会产生误报,那么我们可以增加心跳的打点:

代码语言:js
复制
IJank._heartbeat = () => {
    IJank.log({
        module: 'Jank',
        action: 'heartbeat',
        logTime: xxxxx,
    })
}

当我们心跳产生的时候,会更新堆栈数据。假设发生卡顿的时候,我们拿到这样的堆栈信息:

代码语言:js
复制
_jankLogs = [{
    module: '数据层',
    action: '加载数据',
    logTime: xxxxx,
}, {
    module: 'Jank',
    action: 'heartbeat',
    logTime: xxxxx,
}, {
    module: 'Jank',
    action: 'heartbeat',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '计算',
    logTime: xxxxx,
}, {
    module: 'Jank',
    action: 'heartbeat',
    logTime: xxxxx,
}, {
    module: '渲染层',
    action: '渲染',
    logTime: xxxxx,
}, {
    module: 'Jank',
    action: 'heartbeat',
    logTime: xxxxx,
}, {
    module: '数据层',
    action: '批量操作计算',
    logTime: xxxxx,
}, {
    module: 'Jank',
    action: 'heartbeat',
    logTime: xxxxx,
}]

显然,卡顿发生时最后一次打点为Jank--heartbeat,这意味着卡顿并不是产生于数据层---批量操作计算,而是产生于该逻辑后的一个不知名逻辑。在这种情况下,我们可能还需要再在可疑的地方增加打点,再继续观察。

JavaScript 加载打点

有一个用于监控一些懒加载的 JavaScript 代码的小技巧,我们可以使用PerformanceObserver获取到 JavaScript 代码资源拉取回来后的时机,然后进行打点:

代码语言:js
复制
performanceObserver = new PerformanceObserver((resource) => {
    const entries = resource.getEntries();

    entries.forEach((entry: PerformanceResourceTiming) => {
        // 获取 JavaScript 资源
        if (entry.initiatorType !== 'script') return;
        
        // 打点
        this.log({
            moduleValue: 'compileScript',
            actionValue: entry.name,
        });
    });
});

// 监测 resource 资源
performanceObserver.observe({entryTypes: ['resource']});

当卡顿产生时,堆栈的最后一个日志如果为compileScript--bundle_xxxx之类的,则可以认为该 JavaScript 资源在加载的时候耗时较久,导致卡顿的产生。

通过这样的方式,我们可以有效监控用户卡顿的发生,以及卡顿产生较多的逻辑,然后进行相应的问题定位和优化。

结束语

对于计算逻辑较多、页面逻辑复杂的项目来说,卡顿常常是一个较大痛点。

关于日常性能的数据监控和优化方案之前也有介绍不少,相比一般的性能优化,卡顿往往产生于不合理的逻辑中,比如死循环、过大数据的反复遍历等等,其监控和定位方式也与普通的性能优化不大一致。

查看Github有更多内容噢: https://github.com/godbasin

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 卡顿埋点上报
    • 卡顿打点
      • 心跳打点
        • JavaScript 加载打点
        • 结束语
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档