深入理解 Total Blocking Time(TBT)

在前端性能优化中,Total Blocking Time (TBT) 是一个不容忽视的重要指标。它是衡量网页交互性能的关键指标之一,尤其对单页应用和动态内容页面尤为重要。本文将通过理论解析、代码示例以及优化策略,为您全面剖析 TBT 的本质和优化方法。


什么是 Total Blocking Time?

Total Blocking Time 表示从页面首次开始渲染内容到用户可交互之间,被阻塞时间的总和。具体来说,它计算的是 长任务(任务执行时间超过 50ms)阻塞主线程的时间

  • 核心概念

    • 浏览器主线程任务超过 50ms 的部分,会被认为是阻塞时间。
    • 这些阻塞时间累计起来,就形成了 TBT。

公式

ini 复制代码
TBT = ∑(每个任务耗时 - 50ms),仅计算超过 50ms 的任务

为什么 TBT 重要?

  • 用户体验:TBT 直接影响网页的交互体验,阻塞越多,用户感觉越"卡顿"。
  • Lighthouse 评分:TBT 是 Google Lighthouse 计算性能评分的重要组成部分。
  • 影响 FID(First Input Delay) :TBT 与用户首次交互的延迟密切相关。如果 TBT 长,用户输入可能被延迟处理。

如何测量 TBT?

  1. 使用 Lighthouse: 打开 Chrome DevTools,运行 Lighthouse 报告。在性能评分中,可以看到 TBT 的具体数值。

  2. 使用 WebPageTest: WebPageTest 也可以测量 TBT,并提供详细的阻塞任务分析。

  3. 使用 JavaScript 性能 API: 使用 Performance Observer 捕获长任务的执行时间:

    javascript 复制代码
    const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
            if (entry.duration > 50) {
                console.log(`Long Task Detected: ${entry.duration}ms`);
            }
        }
    });
    
    observer.observe({ type: 'longtask', buffered: true });

TBT 的常见问题及代码示例

1. JavaScript 执行时间过长

  • 问题:JavaScript 阻塞主线程,导致用户无法进行操作。

  • 示例:

    javascript 复制代码
    // 一个耗时过长的同步任务
    function heavyTask() {
        const start = Date.now();
        while (Date.now() - start < 200) {
            // 模拟长任务
        }
    }
    heavyTask();
  • 优化方法: 将长任务拆分为小任务,通过 setTimeoutrequestIdleCallback 优化:

    scss 复制代码
    function heavyTaskOptimized() {
        let start = 0;
        const chunk = 20; // 每次处理 20ms
    
        function processChunk() {
            const now = Date.now();
            while (Date.now() - now < chunk) {
                // 模拟部分任务
                start++;
            }
    
            if (start < 1000) {
                setTimeout(processChunk, 0); // 将剩余任务推迟到下一个事件循环
            }
        }
        processChunk();
    }
    heavyTaskOptimized();

2. 过度渲染导致阻塞

  • 问题:频繁的 DOM 操作占用主线程。

  • 示例:

    ini 复制代码
    // 创建 1000 个 DOM 元素
    for (let i = 0; i < 1000; i++) {
        const div = document.createElement('div');
        div.textContent = `Element ${i}`;
        document.body.appendChild(div);
    }
  • 优化方法:使用 DocumentFragment 或虚拟 DOM 减少渲染阻塞:

    ini 复制代码
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 1000; i++) {
        const div = document.createElement('div');
        div.textContent = `Element ${i}`;
        fragment.appendChild(div);
    }
    document.body.appendChild(fragment);

3. 大量 CSS 计算和重绘

  • 问题:复杂的 CSS 样式或动画阻塞渲染。

  • 示例:

    css 复制代码
    .heavy-animation {
        animation: heavyTask 10s linear infinite;
    }
    
    @keyframes heavyTask {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
  • 优化方法:尽量使用 transformopacity,避免触发布局和重绘:

    css 复制代码
    .optimized-animation {
        animation: optimizedTask 10s linear infinite;
    }
    
    @keyframes optimizedTask {
        0% { transform: translateX(0); }
        100% { transform: translateX(100px); }
    }

优化 TBT 的最佳实践

  1. 减少长任务

    • 将耗时任务拆分为更小的片段。
    • 使用 requestIdleCallback 或 Web Workers 来异步执行任务。
  2. 延迟加载资源

    • 通过动态 import() 延迟加载非关键 JavaScript 模块。

    • 示例:

      ini 复制代码
      import('./heavy-module.js').then(module => {
          module.init();
      });
  3. 优化第三方脚本

    • 删除未使用的脚本,避免引入影响性能的广告或分析工具。
  4. 使用高效的数据处理

    • 使用流处理或增量渲染优化大数据处理。
  5. CSS 和动画优化

    • 使用 GPU 加速的 transformopacity,避免触发重排和重绘。

工具推荐

  1. Lighthouse: Google 官方提供的性能分析工具,可以生成全面的 TBT 报告。

  2. WebPageTest: 高级分析工具,适合测试复杂场景。

  3. Perfume.js: 一个前端性能监控工具,支持自动计算 TBT 和其他指标。

    javascript 复制代码
    import { Perfume } from 'perfume.js';
    const perfume = new Perfume({ firstPaint: true, timeToInteractive: true });
    console.log(`TBT: ${perfume.totalBlockingTime}`);

总结

TBT 是一个反映网页交互性能的重要指标,它不仅影响用户体验,还影响 SEO 和核心 Web 指标的评分。在现代 Web 开发中,通过优化 JavaScript 执行、减少阻塞操作和充分利用异步技术,可以显著降低 TBT 提升性能。

优化 TBT 的过程,既是对技术能力的考验,也是提升用户体验的关键一步。无论是小型项目还是复杂应用,专注于优化 TBT,都会为用户带来更流畅的使用体验。

相关推荐
luoganttcc21 分钟前
Cesium 加载 本地 b3dm 格式文件 并且 获取鼠标点击处经纬度 (亲测可用)
前端·javascript·3d
云边有个稻草人1 小时前
【Web前端技术】第二节—HTML标签(上)
前端·html·html基本结构标签·html超链接标签·html中的注释和特殊字符·vscode的使用·vscode生成骨架标签
介si啥呀~1 小时前
解决splice改变原数组的BUG(拷贝数据)
java·前端·bug
太阳花ˉ1 小时前
BFC详解
前端
小满zs3 小时前
React-router v7 第五章(路由懒加载)
前端·react.js
Aotman_3 小时前
Vue el-from的el-form-item v-for循环表单如何校验rules(二)
前端·javascript·vue.js
BillKu4 小时前
Vue3父子组件数据双向绑定示例
javascript·vue.js·elementui
在无清风5 小时前
Java实现Redis
前端·windows·bootstrap
耕耘虫6 小时前
解决 .Net 6.0 项目发布到IIS报错:HTTP Error 500.30
网络·网络协议·http·iis·net·静态文件·.net 6.0
_一条咸鱼_7 小时前
Vue 配置模块深度剖析(十一)
前端·javascript·面试