Signatur
Beschreibung
Die schreibgeschützte Eigenschaft sourceURL eines PerformanceScriptTiming-Eintrags enthält die URL der Skriptdatei, aus der der gemessene Skriptabschnitt stammt. PerformanceScriptTiming-Objekte bekommen Sie nicht direkt über die Performance Timeline. Sie stecken im Array scripts eines PerformanceLongAnimationFrameTiming-Eintrags, also eines Eintrags vom Typ "long-animation-frame".
Wofür man das braucht: Wenn die Seite ruckelt oder der INP-Wert (Interaction to Next Paint) schlecht ist, zeigt sourceURL, welche Datei für die blockierende Arbeit verantwortlich ist. Das kann ein eigenes Bundle sein, ein Drittanbieter-Tag, ein Werbeskript oder eine Analytics-Bibliothek. Zusammen mit sourceFunctionName und sourceCharPosition lässt sich die Stelle im Code ziemlich genau eingrenzen.
Typischerweise wertet man die Eigenschaft in einem PerformanceObserver aus, gruppiert die Dauer nach sourceURL und schickt die Ergebnisse an ein RUM-Backend (Real User Monitoring). Kann der Browser keine Quelle bestimmen, ist der Wert ein leerer String "". Das passiert zum Beispiel bei Inline-Event-Handlern oder bei Code, der über eval() erzeugt wurde.
Rückgabewert
"", wenn keine Quelle ermittelt werden kann.Beispiele
Skript-URLs langer Animationsframes protokollieren
const observer = new PerformanceObserver((list) => {
for (const frame of list.getEntries()) {
for (const script of frame.scripts) {
console.log(
`${script.sourceURL || '(unbekannt)'} – ${Math.round(script.duration)} ms`,
script.sourceFunctionName
);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Blockierzeit pro Skriptdatei zusammenfassen
const zeitProSkript = new Map();
new PerformanceObserver((list) => {
for (const frame of list.getEntries()) {
for (const { sourceURL, duration } of frame.scripts) {
const key = sourceURL ? new URL(sourceURL).pathname : '(inline/unbekannt)';
zeitProSkript.set(key, (zeitProSkript.get(key) ?? 0) + duration);
}
}
}).observe({ type: 'long-animation-frame', buffered: true });
// Später, z. B. beim Verlassen der Seite
addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const top = [...zeitProSkript].sort((a, b) => b[1] - a[1]).slice(0, 5);
console.table(top);
}
});
Drittanbieter-Skripte erkennen und melden
const eigeneHerkunft = location.origin;
new PerformanceObserver((list) => {
const fremd = list.getEntries()
.flatMap((frame) => frame.scripts)
.filter((s) => s.sourceURL && new URL(s.sourceURL).origin !== eigeneHerkunft)
.map((s) => ({ url: s.sourceURL, dauer: Math.round(s.duration), typ: s.invokerType }));
if (fremd.length) {
navigator.sendBeacon('/rum/loaf', JSON.stringify(fremd));
}
}).observe({ type: 'long-animation-frame', buffered: true });
// Wichtig · Fallstricke
- Kompatibilität: Die Eigenschaft ist experimentell und gehört zur Long Animation Frames API. Unterstützt wird sie nur in Chromium-Browsern (Chrome/Edge ab 123). Prüfen Sie die Unterstützung vorher, z. B. mit
PerformanceObserver.supportedEntryTypes.includes('long-animation-frame'). - Leerer String: Bei Inline-Skripten, Inline-Event-Handlern,
eval()/new Function()oder unbekannter Herkunft istsourceURLoft"". Fangen Sie das ab, bevor Sienew URL()aufrufen, denn sonst wird einTypeErrorausgelöst. - Cross-Origin: Für fremde Skripte, die ohne CORS geladen werden (ohne
crossorigin-Attribut und passendenAccess-Control-Allow-Origin-Header), liefert der Browser unter Umständen nur eingeschränkte oder gar keine Angaben zur Quelle. - Nur lange Skripte: In
scriptslanden nur Skriptabschnitte ab etwa 5 ms Dauer in Frames, die länger als 50 ms dauern. Kurze Aufrufe fehlen also. - Datenschutz: URLs können Query-Parameter mit Tokens oder personenbezogenen Daten enthalten. Kürzen Sie sie vor dem Versand an ein Backend, z. B. auf
origin + pathname.