Start · Sprachen · JavaScript · Referenz · PerformanceLongAnimationFrameTiming.scripts

PerformanceLongAnimationFrameTiming.scripts

Eigenschaft

Gibt ein schreibgeschütztes Array von <code>PerformanceScriptTiming</code>-Objekten zurück, die die Skripte beschreiben, die zu einem langen Animationsframe beigetragen haben.

seit JavaScript Chrome/Edge 123+ (Long Animation Frames API), nich Kategorie: webapi

Signatur

PerformanceLongAnimationFrameTiming.scripts

Beschreibung

Die schreibgeschützte Eigenschaft scripts eines PerformanceLongAnimationFrameTiming-Eintrags (Eintragstyp "long-animation-frame", kurz LoAF) enthält eine Liste von PerformanceScriptTiming-Objekten. Jedes Objekt steht für ein Skript, das während des langen Frames ausgeführt wurde und dabei mindestens 5 Millisekunden gedauert hat.

Genau damit wird die Long Animation Frames API so nützlich: Man erfährt nicht nur, dass ein Frame länger als 50 ms gebraucht hat, sondern auch, welcher Code verantwortlich war. Jedes Script-Timing liefert unter anderem invoker (z. B. "BUTTON#submit.onclick"), invokerType, sourceURL, sourceFunctionName, sourceCharPosition, duration, forcedStyleAndLayoutDuration und pauseDuration.

Typischerweise verwendet man die Eigenschaft im Real User Monitoring (RUM): Mit einem PerformanceObserver sammelt man lange Frames, wertet scripts aus und schickt die Angaben zu den Verursachern, etwa Drittanbieter-Skripte oder teure Event-Handler, an einen Analyse-Endpunkt. So lassen sich schlechte INP-Werte (Interaction to Next Paint) gezielt beheben.

Rückgabewert

Typ
ReadonlyArray<PerformanceScriptTiming>
Beschreibung
Ein eingefrorenes Array von PerformanceScriptTiming-Objekten. Es ist leer, wenn kein Skript im Frame mindestens 5 ms gedauert hat, zum Beispiel wenn Rendering oder Layout den Frame lang gemacht haben.

Beispiele

Lange Frames beobachten und Skripte auflisten

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`Langer Frame: ${Math.round(entry.duration)} ms`);
    for (const script of entry.scripts) {
      console.log(
        `  ${script.invokerType} | ${script.invoker} | ` +
        `${Math.round(script.duration)} ms | ${script.sourceURL}`
      );
    }
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
Langer Frame: 132 ms event-listener | BUTTON#save.onclick | 98 ms | https://example.com/app.js

Teuerstes Skript pro Frame an ein RUM-Backend senden

const report = (data) => {
  navigator.sendBeacon('/rum/loaf', JSON.stringify(data));
};

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.scripts.length === 0) continue;

    const worst = entry.scripts.reduce((a, b) =>
      b.duration > a.duration ? b : a
    );

    report({
      frameDuration: Math.round(entry.duration),
      blocking: Math.round(entry.blockingDuration),
      invoker: worst.invoker,
      source: worst.sourceURL,
      fn: worst.sourceFunctionName,
      charPos: worst.sourceCharPosition,
      forcedLayout: Math.round(worst.forcedStyleAndLayoutDuration)
    });
  }
}).observe({ type: 'long-animation-frame', buffered: true });

Skriptzeit nach Herkunft (erst- vs. drittanbieter) summieren

const totals = new Map();

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    for (const { sourceURL, duration } of entry.scripts) {
      const origin = sourceURL ? new URL(sourceURL).origin : '(unbekannt)';
      totals.set(origin, (totals.get(origin) ?? 0) + duration);
    }
  }
  console.table(
    [...totals].map(([origin, ms]) => ({ origin, ms: Math.round(ms) }))
  );
}).observe({ type: 'long-animation-frame', buffered: true });
┌─────────┬──────────────────────────────┬─────┐ │ (index) │ origin │ ms │ │ 0 │ https://example.com │ 210 │ │ 1 │ https://cdn.tracker.example │ 145 │ └─────────┴──────────────────────────────┴─────┘

// Wichtig · Fallstricke

  • Browser-Kompatibilität: Die Eigenschaft gibt es nur in Chromium-Browsern (ab Chrome/Edge 123). Prüfe die Unterstützung vorher mit PerformanceObserver.supportedEntryTypes.includes('long-animation-frame').
  • 5-ms-Schwelle: Skripte, die kürzer als 5 ms laufen, werden nicht erfasst. Die Summe der scripts[i].duration ist deshalb oft kleiner als die Dauer des Frames.
  • Cross-Origin-Skripte: Bei fremden Skripten ohne CORS (crossorigin-Attribut und passendem Access-Control-Allow-Origin) sind Detailangaben wie sourceFunctionName oder sourceCharPosition leer bzw. -1. Skripte aus Cross-Origin-iframes erscheinen gar nicht.
  • Kein Aufrufstapel: Es wird nur der Einstiegspunkt erfasst, z. B. der Event-Handler oder Callback. Tief verschachtelte Verursacher sieht man nicht direkt.
  • Leeres Array: Ein langer Frame kann auch durch Rendering, Style oder Layout entstehen. Dann ist scripts leer. Werte in diesem Fall renderStart und styleAndLayoutStart aus.
  • Performance: Nutze buffered: true, um frühe Frames nicht zu verpassen. Sende die Daten gebündelt per navigator.sendBeacon() statt bei jedem Eintrag.