Detecta el estado de visibilidad inicial

Determinar si una página se cargó inicialmente en segundo plano (p.ej., si se abrió en una pestaña nueva en segundo plano) es fundamental para supervisar el rendimiento con precisión. Las páginas que se cargan en segundo plano suelen tener un renderizado retrasado y tiempos de métricas más largos (como el Primer procesamiento de imagen con contenido). Identificar estas páginas te permite excluirlas de las estadísticas de rendimiento para evitar datos sesgados.

La forma más precisa de medir esto es con la API de VisibilityStateEntry, que registra de manera confiable los cambios de visibilidad en el cronograma de rendimiento del navegador, independientemente de cuándo se ejecute la secuencia de comandos.

Cómo detectar la visibilidad inicial y el tiempo en segundo plano

OBLIGATORIO: Usa performance.getEntriesByType('visibility-state') para acceder al historial de visibilidad exacto. No te bases únicamente en la verificación de document.visibilityState en el momento de la ejecución, ya que es susceptible a condiciones de carrera.

/**
 * Accurately determines visibility state history using the Performance API.
 */
function getVisibilityInfo() {
  // Retrieve VisibilityStateEntry members:
  const entries = performance.getEntriesByType('visibility-state');
  
  if (entries.length > 0) {
    const firstEntry = entries[0];
    
    // If the first performance entry for visibility is 'hidden',
    // the page was loaded in the background.
    const initiallyBackgrounded = firstEntry.name === 'hidden';
    
    // Find the precise, high-resolution timestamp of when the page 
    // was first backgrounded.
    let timeBackgrounded = null;
    for (const entry of entries) {
      if (entry.name === 'hidden') {
        // entry.startTime is used because it provides the exact browser 
        // timestamp of the visibility change, which is required for precision
        timeBackgrounded = entry.startTime;
        break;
      }
    }

    return {
      initiallyBackgrounded,
      timeBackgrounded
    };
  }
}

Estrategias de resguardo

En el caso de los entornos no compatibles, puedes recurrir a verificar la propiedad document.visibilityState o escuchar el evento visibilitychange.

OBLIGATORIO: Debes comprender que este enfoque de resguardo suele ser muy impreciso para determinar el estado inicial en segundo plano. Debido a que las secuencias de comandos se pueden cargar y ejecutar de forma asíncrona, es posible que se abra una página en una pestaña en segundo plano y, luego, el usuario la ponga en primer plano antes de que la secuencia de comandos termine de descargarse y ejecutarse. Cuando finalmente se ejecute tu secuencia de comandos, document.visibilityState se leerá de forma síncrona como 'visible', y asumirás de forma incorrecta que la página se cargó en primer plano, por lo que se omitirá por completo su estado oculto inicial. Además, la marca de tiempo de resguardo carece de la precisión interna de la API de Performance. Si la precisión es una prioridad alta, no uses el resguardo.

/**
 * Fallback implementation using document.visibilityState.
 * This approach is prone to race conditions if the script loads asynchronously.
 */
function getFallbackVisibilityInfo() {
  // Check the state exactly when this script executes.
  // This will fail to detect an initial background state if the user 
  // foregrounded the page before this script executed.
  let initiallyBackgrounded = document.visibilityState === 'hidden';
  
  // If it's hidden now, we approximate that it was hidden from load (time 0).
  let timeBackgrounded = initiallyBackgrounded ? 0 : null;

  // Listen for future visibility changes to capture if it is backgrounded later.
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden' && timeBackgrounded === null) {
      // performance.now() is used here as a fallback, but it only gives 
      // us the time the event listener fired, not the precise internal 
      // browser time the visibility actually changed.
      timeBackgrounded = performance.now();
    }
  });

  return {
    get initiallyBackgrounded() { return initiallyBackgrounded; },
    get timeBackgrounded() { return timeBackgrounded; }
  };
}

// Modern implementation using VisibilityStateEntry API.
function getVisibilityInfo() {
  // Code omitted here would be the same modern
  // implementation shown earlier in this guide
  // ...
}

// DO: Detect if the VisibilityStateEntry API is available
if ('VisibilityStateEntry' in window) {
  // DO: If VisibilityStateEntry is available, use it first:
  getVisibilityInfo();
} else {
  // DO: If VisibilityStateEntry is unavailable, fall back to `document.visibilityState`:
  getFallbackVisibilityInfo();
}