How to Diagnose Frontend Apps That Get Slower the Longer They Run
From Flame Graphs to Code: How to Pinpoint the 'Slows Down Over Time' Frontend Performance Bottleneck
In daily frontend development, we often encounter a frustrating problem: a page is silky smooth when first opened, but as the user spends more time on it, it gradually becomes sluggish — scrolling is no longer fluid, and click responses become delayed. This phenomenon is usually related to the browser's main thread being occupied for long periods, memory leaks, or frequent unnecessary re-renders.
This article will use Chrome DevTools' Performance panel as the core tool, combined with real-world cases, to guide you step-by-step in mastering how to use flame graphs and the Call Tree to precisely locate and resolve these persistent performance issues.
1. Getting to Know the Performance Profiling Tool: The Performance Panel
To conduct performance profiling, you first need to understand our "microscope" — Chrome DevTools' Performance panel.
1. Core Functionality Overview
The Performance panel is primarily used to record and analyze various performance metrics of a webpage during runtime, including:
- FPS (Frames Per Second) : Frame rate, measuring animation smoothness.
- CPU Load: CPU usage.
- Main Thread Activity: Shows key operations such as JavaScript execution, style calculation, layout, and painting.
- Memory: Memory usage.
2. Basic Interpretation of the Flame Chart
In the flame chart of the Main thread, the horizontal axis represents the timeline, and the vertical axis represents the call stack (i.e., the nesting level of the code). Different colors represent different types of work:
- 🟨 Yellow (Scripting) : JavaScript code execution. Large blocks of yellow here indicate heavy business logic (e.g., complex loop calculations, large-scale data processing).
- 🟣 Purple (Rendering) : Style calculation (Recalculate Style) and Layout (Layout/Reflow). Tall blocks here mean the browser is frequently recalculating element styles and positions.
- 🟩 Green (Painting) : Painting and compositing. Drawing pixels onto the screen.
- 🔴 Red Warning: Usually indicates a Long Task or a Forced Reflow.
2. Hands-On Practice: A Macro-to-Micro Investigation Process
Faced with a complex flame graph, how should we proceed? Here is a standard investigation process.
1. Macro-Level Location: Finding Long Tasks
The most direct cause of page jank is often Long Tasks. In the Main thread, we need to look for color blocks that are very long in duration and very wide. Typically, tasks exceeding 50ms are considered Long Tasks, and Chrome sometimes marks them with a red triangle warning icon in the upper right corner or above these long blocks. It is these long tasks that block the browser's main thread, preventing the page from responding to user interactions in time, thus causing the feeling of sluggishness.
2. Micro-Level Location: Call Tree and Bottom-Up
When we find a Long Task block that causes jank, we need to drill down into it to find the culprit. This is where the Summary, Bottom-up, or Call tree panels below come in handy.
- Call tree: Shows the complete function call chain that led to this long task. You can expand the call stack layer by layer to see which step consumed the most time.
- Bottom-up: This is a very practical view. It merges identical tasks within that time period and sorts them by "Self Time" from high to low. This helps you quickly pinpoint exactly which specific business function or low-level method consumed the vast majority of CPU time.
3. Advanced Techniques and Insights: Four Strategies to Solve 'Slows Down Over Time'
For the phenomenon of "the page only gets slow after being used for a while," besides routine investigation, we need to combine some specific techniques.
1. Beware of Memory Leaks (Combined with the Memory Panel)
The most common reason a page slows down over time is memory leaks.
- Investigation Method: Switch to the Memory panel next to the Performance panel. Check the Memory option when recording.
- Judgment Basis: If, during your continuous operation of the page, the memory curve shows a stepwise, continuously rising trend, and even if you switch to a blank page and back, the memory does not drop, there is a very high probability of a memory leak (e.g., undestroyed event listeners, closure references, uncleaned timers).
2. Pay Attention to Zone.js Callbacks (For Modern Frontend Frameworks)
During investigation, you might find an entry named globalZoneAwareCallback zone.js:1724:1 in the Call tree. zone.js is the core library used by the Angular framework to handle asynchronous operation contexts and is also common in modern frontend projects (especially those based on Webpack/Vite).
- Hidden Danger: If your page binds a large number of
setInterval,setTimeout, or executes heavy DOM operations or state updates in frequent asynchronous request callbacks (Promise.then), these will all be captured byzone.jsand queued for execution on the main thread. - Optimization Suggestion: In the Call tree, trace upwards from
zone.jsorsetIntervalto see which business logic module is constantly triggering asynchronous callbacks, and whether the callback function's execution time is too long. Consider usingclearIntervalto clean up promptly, or offloading heavy operations to a Web Worker.
3. Debounce and Throttle (For High-Frequency Events)
If jank occurs during scrolling (scroll), window resizing (resize), or mouse movement (mousemove), it is usually because these events trigger at a very high frequency, causing the callback function to execute frequently.
- Investigation Method: Check the Call tree. If you find an event handler function (like
onScroll) called hundreds or thousands of times, and each call takes a long time. - Optimization Suggestion: Use Debounce or Throttle to limit the callback execution frequency. For example, in a scroll event, do not frequently modify the DOM or perform complex calculations directly in the callback; instead, record the state first, then process it uniformly in the next
requestAnimationFrame.
4. Long Task Splitting and Avoiding Forced Synchronous Layout
- Long Task Splitting: If a yellow Scripting block is too large (e.g., initializing a table with tens of thousands of data rows), completely exceeding the 50ms limit, you can use
setTimeoutorrequestIdleCallbackto split the large task into multiple smaller tasks, executing them in batches during the browser's idle time to avoid monopolizing the main thread all at once. - Avoiding Forced Synchronous Layout (Layout Thrashing) : If you see many purple Layout blocks in the flame graph, interspersed repeatedly among yellow Scripting blocks, this is usually because the JavaScript code alternates between reading DOM layout properties (like
offsetHeight,scrollTop,getBoundingClientRect) and writing DOM styles. To get the latest value, the browser is forced to recalculate the layout immediately after each write. - Optimization Strategy: Adopt a "batch read first, then batch write" strategy, or use libraries like
FastDomto isolate read and write operations.
4. Summary
Frontend performance optimization is a long-term battle, and the Performance panel is our most powerful weapon. By skillfully using flame graphs, call trees, and memory analysis tools, we can, like a doctor, see through the symptoms to the essence and precisely locate the code lesions causing page jank.
Remember, the core principle of performance optimization is: Measure first, then optimize, and finally verify. Do not guess blindly; let the data tell you where to start. I hope the sharing in this article can help you face the 'slows down over time' problem with confidence and skill.