Navegadores compatibles
En la actualidad, los navegadores modernos a veces suspenden páginas o las descartan por completo cuando los recursos del sistema están limitados. En el futuro, los navegadores querrán hacer esto de forma proactiva, por lo que consumen menos energía y memoria. La API de Page Lifecycle proporciona hooks de ciclo de vida para que tus páginas puedan manejar estos navegadores de forma segura sin afectar la experiencia del usuario. Consulta la API para ver si deberías implementar estas funciones en tu aplicación.
Información general
El ciclo de vida de las aplicaciones es una forma clave en la que los sistemas operativos modernos administran de Google Cloud. En Android, iOS y versiones recientes de Windows, las apps pueden iniciarse y el sistema operativo en cualquier momento. Esto permite que estas plataformas optimicen y reasignar los recursos donde mejor beneficien al usuario.
Históricamente no existía ese ciclo de vida en la Web, y las apps se pueden vivan indefinidamente. Al ejecutar una gran cantidad de páginas web, el sistema crítico recursos como la memoria, la CPU, la batería y la red pueden suscribirse en exceso, lo que generará una mala experiencia del usuario final.
Mientras que la plataforma web tiene durante mucho tiempo eventos relacionados con los estados del ciclo de vida
— como load
,
unload
y
visibilitychange
: estos eventos solo permiten que los desarrolladores
para responder a cambios de estado
del ciclo de vida iniciados por el usuario. Para que la Web funcione
de manera confiable en dispositivos de baja energía (y ser más consciente de los recursos en general en
todas las plataformas) necesitan una forma de reclamar y reasignar proactivamente
de Google Cloud.
De hecho, los navegadores actuales ya toman medidas activas para conservar los recursos para páginas en pestañas en segundo plano, y a muchos navegadores (especialmente Chrome) les gustaría para hacer mucho más y disminuir su huella de recursos general.
El problema es que los desarrolladores no tienen forma de prepararse para este tipo de intervenciones iniciadas por el sistema ni saber si están sucediendo. Esto significa deben ser conservadores para no atacar las páginas web.
La API de Page Lifecycle intenta resolver este problema:
- Presentación y estandarización del concepto de estados del ciclo de vida en la Web.
- Definición de nuevos estados iniciados por el sistema que permiten a los navegadores limitar la recursos que pueden consumir las pestañas ocultas o inactivas.
- Crear nuevas API y eventos que permitan a los desarrolladores web responder a y las transiciones hacia y desde estos nuevos estados iniciados por el sistema.
Esta solución proporciona la previsibilidad que los desarrolladores web necesitan para compilar más resistentes a las intervenciones del sistema y permite que los navegadores optimizar agresivamente los recursos del sistema, lo que, en última instancia, beneficia a todos los usuarios web.
El resto de esta publicación presentará las nuevas funciones del ciclo de vida de la página y exploraremos su relación con todos los estados existentes de las plataformas web y eventos. También dará recomendaciones y prácticas recomendadas para los tipos de trabajo que los desarrolladores deberían (y no deberían) hacer en cada estado.
Descripción general de los estados y eventos del ciclo de vida de la página
Todos los estados del ciclo de vida de la página son discretos y mutuamente excluyentes, es decir, solo pueden estar en un estado a la vez. Y la mayoría de los cambios en el estado del ciclo de vida de una página generalmente son observables a través de eventos del DOM (consulta las recomendaciones para desarrolladores para cada estado para conocer las excepciones).
Tal vez la forma más fácil de explicar los estados del ciclo de vida de la página, así como los eventos que señalan las transiciones entre ellos, es con un diagrama:
Estados
En la siguiente tabla, se explica cada estado en detalle. También enumera las posibles estados que pueden venir antes y después, así como los eventos que para observar los cambios.
Estado | Descripción |
---|---|
Activo |
Una página se encuentra en estado activa si es visible y tiene enfoque de entrada.
Posibles estados anteriores: |
Pasivo |
Una página se encuentra en estado pasivo si es visible y no tienen foco de entrada.
Posibles estados anteriores:
Próximos estados posibles: |
Oculto |
Una página se encuentra en estado oculta si no está visible (y no está se hayan congelado, descartado o cancelado).
Posibles estados anteriores:
Próximos estados posibles: |
Inhabilitado |
En estado congelado, el navegador suspende la ejecución de
congelable
.
tareas en el directorio
listas de tareas en cola hasta que la página se desbloquee. Esto significa cosas como
No se ejecutan los cronómetros de JavaScript ni las devoluciones de llamada de recuperación. Ya se está ejecutando
tareas pueden finalizar (lo más importante es el
Los navegadores suspenden páginas para preservar el uso de la CPU, la batería y los datos. no también pueden hacerlo como una forma de habilitar las navegaciones hacia atrás/adelante: Evitan la necesidad de una página completa. volver a cargar la página.
Posibles estados anteriores:
Próximos estados posibles: |
Cancelado |
La página se encuentra en el estado terminada una vez que comienza a se descargan de la memoria y se borran de la memoria. No tareas nuevas pueden comenzar en este estado, y las tareas en curso pueden matan si pasan demasiado tiempo.
Posibles estados anteriores:
Próximos estados posibles: |
Descartado |
Una página se encuentra en el estado descartada cuando la descarga la navegador para conservar los recursos. No se requieren tareas, devoluciones de llamadas de eventos JavaScript de cualquier tipo puede ejecutarse en este estado, ya que, por lo general, se descarta ocurren bajo restricciones de recursos, en las que iniciar procesos nuevos es imposible. En el estado discarded, la pestaña en sí (incluidos el título de la pestaña y el ícono de página) suele ser visible para el usuario aunque la página haya desaparecido.
Posibles estados anteriores:
Próximos estados posibles: |
Eventos
Los navegadores envían muchos eventos, pero solo una pequeña parte de ellos señala un posible cambio en el estado del ciclo de vida de la página. En la siguiente tabla, se describen todos los eventos relacionados con el ciclo de vida y enumera de qué estados pueden pasar.
Nombre | Detalles |
---|---|
focus
|
Se centró un elemento del DOM.
Nota: Un evento
Posibles estados anteriores:
Posibles estados actuales: |
blur
|
Un elemento del DOM perdió el foco.
Nota: Un evento
Posibles estados anteriores:
Posibles estados actuales: |
visibilitychange
|
El nombre del documento
Cambió el valor de |
freeze
*
|
La página se acaba de bloquear. Cualquiera no se iniciará la tarea freezable de las listas de tareas en cola de la página.
Posibles estados anteriores:
Posibles estados actuales: |
resume
*
|
El navegador reanudó una página inmovilizada.
Posibles estados anteriores:
Posibles estados actuales: |
pageshow
|
Se está accediendo a una entrada del historial de sesión. Puede ser una carga de página completamente nueva o una página extraída de la
memoria caché atrás/adelante. Si la página
se extrajo de la Memoria caché atrás/adelante, la ubicación
La propiedad
Posibles estados anteriores: |
pagehide
|
Se realiza un recorrido de una entrada del historial de sesión. Si el usuario navega a otra página y el navegador puede agregar
la página actual con la función atrás/adelante
caché para volver a usarse más adelante, la propiedad
Posibles estados anteriores:
Posibles estados actuales: |
beforeunload
|
La ventana, el documento y sus recursos están a punto de descargarse. El documento sigue siendo visible y el evento se puede cancelar en esta punto.
Importante: El evento
Posibles estados anteriores:
Posibles estados actuales: |
unload
|
Se está descargando la página.
Advertencia:
nunca se recomienda usar el evento
Posibles estados anteriores:
Posibles estados actuales: |
* Indica un evento nuevo definido por la API de Page Lifecycle.
Nuevas funciones agregadas en Chrome 68
El gráfico anterior muestra dos estados iniciados por el sistema en lugar de iniciado por el usuario: congelado y descartado. Como mencionamos, en la actualidad, los navegadores a veces se congelan y descartan pestañas ocultas (a su discreción), pero los desarrolladores no tienen forma de saber cuándo esto está sucediendo.
En Chrome 68, los desarrolladores ahora pueden ver si una pestaña oculta se inmoviliza y
se descongelará escuchando el freeze
y resume
el document
.
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
A partir de Chrome 68, el objeto document
ahora incluye un
wasDiscarded
en Chrome para computadoras de escritorio (en este caso, se hace un seguimiento de la compatibilidad de Android en este problema). Para determinar si una página se descartó mientras estaba oculta
, puedes inspeccionar el valor de esta propiedad cuando se cargue la página. (Nota:
las páginas descartadas se deben volver a cargar para usarlas otra vez).
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
Para obtener consejos sobre lo que es importante hacer en la freeze
y la resume
eventos, y cómo manejar y prepararte para las páginas que se descartan, consulta
recomendaciones para desarrolladores
para cada estado.
Las siguientes secciones ofrecen una descripción general de cómo encajan estas nuevas funciones en los estados y eventos de la plataforma web existente.
Cómo observar los estados del ciclo de vida de las páginas en el código
En el modo activo, pasivo y oculto, estados, es posible ejecutar código JavaScript que determine la configuración Estado del ciclo de vida de la página desde las APIs de plataformas web existentes
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
Los estados congelado y finalizado en el
por otro lado, solo se puede detectar en su receptor de eventos respectivo
(freeze
y pagehide
) como el estado es
cambian.
Cómo observar los cambios de estado
Si compilas en la función getState()
definida anteriormente, puedes observar toda la página
El estado del ciclo de vida cambia con el siguiente código.
// Stores the initial state using the `getState()` function (defined above).
let state = getState();
// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the `state` value defined above.
const logStateChange = (nextState) => {
const prevState = state;
if (nextState !== prevState) {
console.log(`State change: ${prevState} >>> ${nextState}`);
state = nextState;
}
};
// Options used for all event listeners.
const opts = {capture: true};
// These lifecycle events can all use the same listener to observe state
// changes (they call the `getState()` function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, () => logStateChange(getState()), opts);
});
// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
// In the freeze event, the next state is always frozen.
logStateChange('frozen');
}, opts);
window.addEventListener('pagehide', (event) => {
// If the event's persisted property is `true` the page is about
// to enter the back/forward cache, which is also in the frozen state.
// If the event's persisted property is not `true` the page is
// about to be unloaded.
logStateChange(event.persisted ? 'frozen' : 'terminated');
}, opts);
Este código hace tres cosas:
- Establece el estado inicial con la función
getState()
. - Define una función que acepta el siguiente estado y, si hay un cambio, registra los cambios de estado en la consola.
- Elementos agregados
captura de pantalla
de eventos de ciclo de vida para todos los eventos necesarios del ciclo de vida, que a su vez llaman
logStateChange()
, y pasa al siguiente estado.
Debes tener en cuenta que todos los objetos de escucha de eventos se agregan
a window
y todas pasan
{capture: true}
Esto se debe a varios motivos:
- No todos los eventos del ciclo de vida de la página tienen el mismo objetivo.
pagehide
ypageshow
se activan elwindow
;visibilitychange
,freeze
yresume
se activan eldocument
, yfocus
yblur
se activan en su los elementos del DOM respectivos. - La mayoría de estos eventos no aparecen como burbujas, lo que significa que es imposible agregar sin capturar objetos de escucha de eventos en un elemento principal común y observar todos de ellas.
- La fase de captura se ejecuta antes de las fases objetivo o burbuja, por lo que de objetos de escucha de ese tipo ayuda a garantizar que se ejecuten antes de que otro código pueda cancelarlas.
Recomendaciones para desarrolladores para cada estado
Como desarrolladores, es importante comprender los estados del ciclo de vida de las páginas y saber cómo observarlas en código porque el tipo de trabajo que deberías (y debería no) dependerá en gran medida del estado en el que se encuentre tu página.
Por ejemplo, está claro que no tiene sentido mostrar una notificación transitoria al usuario si la página está oculta. Aunque este ejemplo es bastante obvias, hay otras recomendaciones que no son tan obvias que valen la pena enumeración.
Estado | Recomendaciones para desarrolladores |
---|---|
Active |
El estado active es el momento más crítico para el usuario y, por lo tanto, el momento más importante para que tu página esté responde a las entradas del usuario. Se debe dejar de priorizar cualquier trabajo que no sea de IU que pueda bloquear el subproceso principal. para períodos de inactividad o se descargan a un trabajador web. |
Passive |
En el estado pasivo, el usuario no está interactuando con la página. pero aún pueden verlo. Esto significa que las actualizaciones y animaciones de la IU deben ser fluido, pero el momento en que se producen estas actualizaciones es menos crítico. Cuando la página cambia de activa a pasiva, se genera una es un buen momento para conservar el estado de la aplicación sin guardar. |
Cuando la página cambia de pasiva a oculta, hay una es posible que el usuario no interactúe con él hasta que se vuelva a cargar. La transición a oculto también suele ser el último cambio de estado
que los desarrolladores pueden observar
de forma confiable (esto es especialmente cierto en
móvil, ya que los usuarios pueden cerrar pestañas o la propia app de navegador, y el
Esto significa que debes tratar el estado oculto como el final probable del de la sesión del usuario. En otras palabras, conservar cualquier estado de aplicación sin guardar y envía los datos de estadísticas no enviados. También debes dejar de realizar actualizaciones de la IU (ya que no se verán por el usuario), y debes detener cualquier tarea que un usuario no quiera que se ejecuta en segundo plano. |
|
Frozen |
En estado congelado, las tareas que se pueden inmovilizar . las listas de tareas en cola se suspenden hasta que la página se desbloquee, lo que puede nunca ocurrirán (p.ej., si se descarta la página). Esto significa que cuando la página cambia de oculta a inmovilizada es esencial detener los temporizadores o eliminar las conexiones si se bloquea, podría afectar a otras pestañas abiertas del mismo origen o al capacidad del navegador para colocar la página en la Memoria caché atrás/adelante. En particular, es importante que hagas lo siguiente:
También debes conservar cualquier estado de vista dinámica (p.ej., la posición de desplazamiento)
en una vista de lista infinita)
Si la página pasa de inmovilizada de nuevo a oculta, puedes volver a abrir las conexiones cerradas o reiniciar las encuestas se detuvo cuando la página estaba inicialmente inmovilizada. |
Terminated |
Por lo general, no es necesario que realices ninguna acción cuando se traslada una página al estado terminated. Como las páginas que se descargan como resultado de la acción del usuario siempre van por el estado hidden antes de ingresar a terminated En el estado oculto, se muestra donde la lógica de finalización de la sesión (p.ej., el estado de aplicación persistente y los informes a Analytics) deben una tarea. Además (como se menciona en las recomendaciones para
el estado oculto), es muy importante que los desarrolladores sepan
que la transición al estado finalizado no puede ser de manera confiable
en muchos casos (especialmente en dispositivos móviles), por lo que
en eventos de finalización (p.ej., |
Discarded |
Los desarrolladores no pueden observar el estado descartado en el cada vez que se descarta una página. Esto se debe a que las páginas suelen descartadas bajo restricciones de recursos y descongelar una página solo para permitir de comandos para ejecutar en respuesta a un evento de descarte no es posible en en la mayoría de los casos. Por eso, debes prepararte
ante la posibilidad de un descarte
el cambio de oculto a congelado y, luego,
reacciona al restablecimiento de una página descartada en el momento de carga de la página
verificando |
Una vez más, dado que la confiabilidad y el orden de los eventos de ciclo de vida no es de forma coherente en todos los navegadores, la forma más fácil de seguir los consejos en la tabla es usar PageLifecycle.js.
APIs de ciclo de vida heredadas que se deben evitar
Los siguientes eventos deben evitarse siempre que sea posible.
El evento de descarga
Muchos desarrolladores consideran el evento unload
como una devolución de llamada garantizada y lo usan como
un indicador de fin de sesión para guardar
el estado y enviar datos de estadísticas,
es sumamente poco confiable, sobre todo en dispositivos móviles. El evento unload
no
activar en muchas situaciones típicas de descarga, como cerrar una pestaña desde ella
en un dispositivo móvil o cierra la app de navegador desde el selector de apps.
Por este motivo, siempre es mejor confiar en el
visibilitychange
para determinar cuándo se produce una sesión
y considera el estado oculto que
último tiempo confiable para ahorrar datos de la app y del usuario.
Además, la mera presencia de un controlador de eventos unload
registrado (mediante
onunload
o addEventListener()
) pueden impedir que los navegadores puedan
para colocar páginas en la memoria caché atrás/adelante para agilizar el
hacia adelante y hacia atrás.
En todos los navegadores actualizados, se recomienda usar siempre la
pagehide
para detectar posibles descargas de páginas (también conocidas como
terminated) en lugar del evento unload
. Si
debe ser compatible con Internet Explorer 10 o versiones anteriores, debes incluir
detectar el evento pagehide
y usar solo unload
si el navegador no es compatible
pagehide
:
const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
window.addEventListener(terminationEvent, (event) => {
// Note: if the browser is able to cache the page, `event.persisted`
// is `true`, and the state is frozen rather than terminated.
});
El evento beforeunload
El evento beforeunload
tiene un problema similar al evento unload
.
históricamente, la presencia de un evento beforeunload
podría impedir que las páginas
son aptos para el almacenamiento en la memoria caché atrás/adelante. Navegadores modernos
no tienen esta restricción. Aunque algunos navegadores, como precaución, no se activan.
el evento beforeunload
cuando intentas colocar una página en la función Atrás/Adelante
caché, lo que significa que el evento no es confiable como indicador de fin de sesión.
Además, algunos navegadores (incluido Chrome)
Requerir una interacción del usuario en la página antes de permitir el evento beforeunload
se active, lo que afectará aún más su confiabilidad.
Una diferencia entre beforeunload
y unload
es que hay
usos legítimos de beforeunload
. Por ejemplo, cuando quieres advertir al usuario
de que tienen cambios sin guardar que perderán si siguen descargando la página.
Debido a que existen razones válidas para usar beforeunload
, te recomendamos que
solo agrega objetos de escucha beforeunload
cuando un usuario tenga cambios sin guardar y, luego,
quitarlos inmediatamente después de guardarlos.
En otras palabras, no hagas esto (ya que agrega un objeto de escucha beforeunload
incondicionalmente):
addEventListener('beforeunload', (event) => {
// A function that returns `true` if the page has unsaved changes.
if (pageHasUnsavedChanges()) {
event.preventDefault();
// Legacy support for older browsers.
return (event.returnValue = true);
}
});
En su lugar, haz esto (ya que solo agrega el objeto de escucha beforeunload
cuando es
necesario y la quita cuando no lo es):
const beforeUnloadListener = (event) => {
event.preventDefault();
// Legacy support for older browsers.
return (event.returnValue = true);
};
// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
addEventListener('beforeunload', beforeUnloadListener);
});
// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
removeEventListener('beforeunload', beforeUnloadListener);
});
Preguntas frecuentes
¿Por qué no hay una "carga" estado?
La API de Page Lifecycle define como estados discretos y mutuamente excluyentes. Dado que una página puede cargarse en estado activo, oculto o pasivo, y ya que puede cambiar de estado (o incluso finalizarse) antes de que termine de cargarse, un un estado de carga independiente no tiene sentido en este paradigma.
Mi página realiza un trabajo importante cuando está oculta, ¿cómo puedo evitar que se congele o se descarte?
Existen muchos motivos legítimos por los que las páginas web no deberían inmovilizarse mientras se ejecutan en el estado oculto. El ejemplo más evidente es una app que reproduce música.
También hay situaciones en las que puede ser riesgoso que Chrome descarte una página,
por ejemplo, si contiene un formulario con entradas de usuarios no enviadas o si
beforeunload
, que advierte cuando se descarga la página.
Por el momento, Chrome será conservador al descartar páginas y hacerlo solo cuando esté seguro de que no afectará a los usuarios. Por ejemplo, las páginas que se observó que hace cualquiera de las siguientes acciones mientras está en estado oculto no se descartarán a menos que estén bajo restricciones de recursos extremas:
- Cómo reproducir audio
- Cómo usar WebRTC
- Actualiza el título de la tabla o el ícono de página
- Mostrando alertas
- Envío de notificaciones push
Para conocer las funciones de lista actuales que se usan para determinar si una pestaña puede ser segura congelada o descartada, consulta: Heurística para congelar y Descartando en Chrome.
¿Qué es la Memoria caché atrás/adelante?
La memoria caché atrás/adelante es un término utilizado para describir una La optimización de navegación que implementan algunos navegadores adelantar los botones más rápido.
Cuando un usuario sale de una página, estos navegadores inmovilizan una versión de esa
de manera que se pueda reanudar rápidamente en caso de que el usuario navegue hacia atrás con
los botones para retroceder o adelantar. Recuerda que agregar un unload
controlador de evento evita que se pueda realizar esta optimización.
A todos los efectos, este bloqueo es funcionalmente igual a que los navegadores se inmovilizan para conservar la CPU/batería. Por eso, es se considera parte del estado del ciclo de vida congelado.
Si no puedo ejecutar APIs asíncronas en estado congelado o finalizado, ¿cómo puedo guardar datos en IndexedDB?
En los estados congelados y finalizados, tareas que se pueden inmovilizar en las listas de tareas en cola de una página están suspendidos, lo que significa que las APIs asíncronas y basadas en devoluciones de llamada como IndexedDB no pueden usarse de manera confiable.
En el futuro, agregaremos un método commit()
a los objetos IDBTransaction
, que
Les dan a los desarrolladores una forma de realizar lo que son transacciones de solo escritura.
que no requieren devoluciones de llamada. En otras palabras, si el desarrollador solo está escribiendo
datos a IndexedDB y no realizar una transacción compleja que consista en lecturas
y escrituras, el método commit()
podrá finalizar antes de que las listas de tareas en cola
suspendida (suponiendo que la base de datos IndexedDB ya está abierta).
Sin embargo, para el código que debe funcionar actualmente, los desarrolladores tienen dos opciones:
- Utiliza el almacenamiento de la sesión: Almacenamiento de la sesión es síncrono y persiste cuando se descarta una página.
- Usa IndexedDB de tu service worker: un service worker puede
IndexedDB después de que la página se cerró o se descartó En
freeze
o Objeto de escucha de eventospagehide
con el que puedes enviar datos a tu service workerpostMessage()
: y el service worker puede controlar el guardado de los datos.
Prueba la app en los estados congelado y descartado
Para probar cómo se comporta la app en los estados de bloqueo y descartado, puedes visitar
chrome://discards
para inmovilizar o descartar
pestañas abiertas.
Esto te permitirá asegurarte de que tu página administre correctamente freeze
y resume
eventos, así como la marca document.wasDiscarded
cuando las páginas se vuelven a cargar después de
descartar.
Resumen
Desarrolladores que desean respetar los recursos del sistema de los dispositivos de sus usuarios deberían compilar sus apps teniendo en cuenta los estados del ciclo de vida de la página. Es fundamental que las páginas web no consuman demasiados recursos del sistema en situaciones en las que el usuario no esperaría
Cuanto más desarrolladores comiencen a implementar las nuevas APIs de ciclo de vida de la página, más segura será los navegadores se inmovilizarán y descarten las páginas que no se están utilizando. Esta significa que consumirán menos recursos de memoria, CPU, batería y red lo que beneficia a los usuarios.