Optimizing Total Blocking Time (TBT) for Frontend Applications
Description
Total Blocking Time (TBT) is a key metric for measuring page responsiveness during loading. It is defined as the sum of all blocking times from long tasks (tasks that take longer than 50 milliseconds to execute) between First Contentful Paint (FCP) and Time to Interactive (TTI). For example, an 80-millisecond task contributes 30 milliseconds of blocking time (80-50=30). TBT should be kept under 200 milliseconds to ensure a smooth user experience. The core of optimizing TBT lies in reducing the frequency and duration of long tasks.
Optimization Steps
-
Identify Long Tasks
- Use the Performance panel in Chrome DevTools to record the page loading process, focusing on task bars on the main thread. Tasks marked in red are likely long tasks (exceeding 50 ms).
- Obtain a specific list of long tasks and their blocking times from the "Total Blocking Time" section of a Lighthouse audit.
-
Split Long Tasks into Smaller Ones
- Break down synchronous JavaScript logic into multiple asynchronous tasks. For example, convert large data processing into chunked execution:
// Original long task (assuming 100ms duration) function processData() { for (let i = 0; i < 1e6; i++) { /* intensive computation */ } } // After splitting, use setTimeout for chunking function chunkedProcess(data, chunkSize = 1000) { let index = 0; function nextChunk() { const chunk = data.slice(index, index + chunkSize); chunk.forEach(item => { /* process individual item */ }); index += chunkSize; if (index < data.length) { setTimeout(nextChunk, 0); // Defer remaining tasks to the next event loop } } nextChunk(); } - Use Web Workers to move complex computations off the main thread, preventing blocking.
- Break down synchronous JavaScript logic into multiple asynchronous tasks. For example, convert large data processing into chunked execution:
-
Optimize JavaScript Execution Efficiency
- Reduce unnecessary DOM operations (e.g., frequent style modifications within loops), and consolidate multiple operations into a single update batch.
- Use more efficient algorithms or data structures (e.g., use Map instead of objects for key-value lookups).
- Avoid forced synchronous layouts (e.g., reading then writing layout properties), and batch DOM changes via
requestAnimationFrame.
-
Defer Non-Critical Script Loading
- Load scripts not essential for the initial viewport (e.g., analytics SDKs) using the
asyncordeferattributes:<script src="analytics.js" async></script> - Use dynamic imports (
import()) for on-demand module loading, reducing the initial bundle size.
- Load scripts not essential for the initial viewport (e.g., analytics SDKs) using the
-
Minimize Impact of Third-Party Scripts
- Audit the performance overhead of third-party scripts and remove or replace inefficient resources.
- Use
rel="preconnect"ordns-prefetchto establish connections early, or defer loading non-core scripts.
-
Continuous Monitoring and Testing
- Use Lighthouse CI to detect TBT changes after each code modification.
- Monitor TBT metrics in production environments in real-time using the Web Vitals library:
import {getTTFB, getFCP, getLCP, getTBT} from 'web-vitals'; getTBT(console.log);
By systematically reducing main thread blocking through the steps above, TBT can be significantly lowered, enhancing the user-perceived responsiveness.