Signatur
Beschreibung
Die schreibgeschützte Eigenschaft blockingDuration eines PerformanceLongAnimationFrameTiming-Eintrags (Long Animation Frames API, kurz LoAF) gibt an, wie lange der Hauptthread insgesamt blockiert war. In dieser Zeit konnte er nicht auf Aufgaben mit hoher Priorität wie Nutzereingaben reagieren. Der Wert ist eine Zahl in Millisekunden. Nachkommastellen sind möglich.
Berechnung: Der Browser nimmt alle langen Tasks innerhalb des Frames, also Tasks mit mehr als 50 ms Dauer, und zieht von jeder 50 ms ab. Die Renderzeit des Frames wird zur Dauer des längsten Tasks addiert. Die Summe dieser Werte ist blockingDuration. Ein Frame kann also lang sein (duration > 50 ms) und trotzdem eine blockingDuration von 0 haben, wenn er aus vielen kurzen Tasks besteht.
Die Eigenschaft eignet sich gut, um Probleme bei der Reaktionsfähigkeit zu finden, etwa als Ursache für schlechte Interaction to Next Paint-Werte (INP). Sie misst genauer als duration, wie stark Nutzer tatsächlich ausgebremst werden. Wer sie mit scripts, renderStart und styleAndLayoutStart kombiniert, findet heraus, welcher Code die Blockade verursacht.
Rückgabewert
0, wenn kein einzelner Task länger als 50 ms dauerte.Beispiele
Blockierende Frames mit PerformanceObserver protokollieren
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.blockingDuration > 0) {
console.log(
`Frame ${entry.duration.toFixed(0)} ms lang, davon ${entry.blockingDuration.toFixed(0)} ms blockierend`
);
}
}
});
// buffered: true liefert auch Einträge, die vor dem Registrieren entstanden sind
observer.observe({ type: 'long-animation-frame', buffered: true });
Gesamte Blockierzeit summieren und beim Verlassen der Seite senden
let totalBlocking = 0;
let worstFrame = null;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
totalBlocking += entry.blockingDuration;
if (!worstFrame || entry.blockingDuration > worstFrame.blockingDuration) {
worstFrame = entry;
}
}
}).observe({ type: 'long-animation-frame', buffered: true });
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/analytics/loaf', JSON.stringify({
totalBlocking: Math.round(totalBlocking),
worstBlocking: Math.round(worstFrame?.blockingDuration ?? 0),
worstScripts: worstFrame?.scripts.map((s) => s.sourceURL) ?? []
}));
}
});
Verursachende Skripte des stärksten Blockers ermitteln
const frames = performance.getEntriesByType('long-animation-frame');
const top = frames
.filter((f) => f.blockingDuration > 0)
.sort((a, b) => b.blockingDuration - a.blockingDuration)[0];
if (top) {
console.log(`Blockierend: ${top.blockingDuration.toFixed(1)} ms`);
for (const script of top.scripts) {
console.log(`${script.invoker} → ${script.sourceURL} (${script.duration.toFixed(0)} ms)`);
}
} else {
console.log('Keine blockierenden Frames gefunden');
}
// Wichtig · Fallstricke
- Browser-Unterstützung: Nur in Chromium-Browsern ab Version 123 (Chrome, Edge, Opera). Firefox und Safari unterstützen die API nicht. Vor der Nutzung prüfen, ob der Browser sie kennt:
PerformanceObserver.supportedEntryTypes.includes('long-animation-frame'). durationist nicht gleichblockingDuration: Ein Frame mit vielen kurzen Tasks kann über 50 ms lang sein und trotzdemblockingDuration === 0haben. Wer Reaktionsfähigkeit bewerten will, sollteblockingDurationauswerten.- Kein direkter TBT-Ersatz: Wegen der Renderzeit, die zum längsten Task addiert wird, weichen die Werte von der Total Blocking Time aus Lighthouse und der Long Tasks API ab.
- Puffer-Grenze:
performance.getEntriesByType()liefert nur begrenzt viele gepufferte Einträge. Zum fortlaufenden Messen einenPerformanceObserververwenden. - Genauigkeit: Die Zeitauflösung kann aus Sicherheitsgründen gerundet sein (Schutz vor Timing-Angriffen). In Seiten ohne Cross-Origin-Isolation ist sie gröber.