Compatibilidade com navegadores
Os navegadores modernos às vezes suspendem ou descartam páginas quando os recursos do sistema são restritos. No futuro, os navegadores precisarão fazer isso proativamente, para que consumam menos energia e memória. A API Page Lifecycle fornece ganchos de ciclo de vida para que suas páginas possam lidar com esses navegadores intervenções sem afetar a experiência do usuário. Dê uma olhada na API para verifique se é preciso implementar esses recursos no aplicativo.
Contexto
O ciclo de vida do aplicativo é fundamental para o gerenciamento dos sistemas operacionais do Google Cloud. No Android, no iOS e em versões recentes do Windows, é possível iniciar e interrompido a qualquer momento pelo SO. Isso permite que essas plataformas simplifiquem e realoquem os recursos onde eles melhor beneficiam o usuário.
Historicamente, na Web não existe esse ciclo de vida, e os apps podem ser mantidos vivas indefinidamente. Com um grande número de páginas da Web em execução, sistemas essenciais recursos como memória, CPU, bateria e rede podem ter excesso de assinaturas, levando a uma experiência ruim para o usuário final.
A plataforma da Web já tem eventos relacionados aos estados do ciclo de vida há muito tempo
como load
,
unload
e
visibilitychange
com esses eventos, só os desenvolvedores
para responder a mudanças de estado do ciclo de vida iniciadas pelo usuário. Para que a Web funcione
de forma confiável em dispositivos de baixa potência e, em geral, priorizar os recursos
todas as plataformas) os navegadores precisam de uma forma de recuperar e realocar proativamente
do Google Cloud.
Na verdade, hoje em dia, os navegadores já tomam medidas ativas para economizar recursos. para páginas em guias de fundo, e muitos navegadores (especialmente o Chrome) gostariam para fazer muito mais disso — para diminuir a pegada geral de recursos.
O problema é que os desenvolvedores não têm como se preparar para esses tipos de de intervenções iniciadas pelo sistema ou mesmo saber que elas estão acontecendo. Isso significa que navegadores devem ser conservadores ou correr o risco de corromper páginas da web.
A API Page Lifecycle tenta resolver esse problema da seguinte forma:
- Introdução e padronização do conceito de estados do ciclo de vida na Web.
- Definir novos estados iniciados pelo sistema que permitem que os navegadores limitem a recursos que podem ser consumidos por guias ocultas ou inativas.
- Criar novas APIs e eventos que permitem aos desenvolvedores Web responder transita de e para esses novos estados iniciados pelo sistema.
Essa solução oferece a previsibilidade que os desenvolvedores Web precisam para criar resilientes a intervenções do sistema e permite que os navegadores otimizar de forma agressiva os recursos do sistema, beneficiando todos os usuários da Web.
O restante desta postagem vai apresentar os novos recursos de ciclo de vida da página e vamos descobrir como eles se relacionam com todos os estados atuais da plataforma da Web. e eventos. Ele também traz recomendações e práticas recomendadas para os tipos de trabalho que os desenvolvedores deveriam (e não deveriam) estar fazendo em cada estado.
Visão geral de eventos e estados do ciclo de vida da página
Todos os estados do ciclo de vida da página são discretos e mutuamente exclusivos, o que significa que uma página só podem estar em um estado por vez. E a maioria das mudanças no estado do ciclo de vida de uma página são geralmente observáveis por meio de eventos DOM (consulte as recomendações do desenvolvedor para cada estado para ver as exceções).
Talvez a maneira mais fácil de explicar os estados do ciclo de vida da página e eventos que sinalizam transições entre elas, é com um diagrama:
Estados
A tabela a seguir explica cada estado em detalhes. Ele também lista os possíveis estados que podem vir antes e depois, bem como os eventos que os desenvolvedores podem usar para observar as mudanças.
Estado | Descrição |
---|---|
Ativa |
Uma página vai estar no estado ativo se estiver visível e tiver o foco de entrada.
Possíveis estados anteriores: |
Passivo |
Uma página vai estar no estado passivo se estiver visível e não têm foco de entrada.
Possíveis estados anteriores:
Próximos estados possíveis: |
Ocultos |
Uma página fica no estado oculto quando não está visível (e não congelados, descartados ou encerrados).
Possíveis estados anteriores:
Próximos estados possíveis: |
Congelado |
No estado congelado, o navegador suspende a execução de
congelável
tarefas no
filas de tarefas até que a página seja descongelada. Isso significa que itens como
Os timers do JavaScript e os callbacks de busca não são executados. Já em execução
tarefas podem terminar (o mais importante é o
callback Os navegadores congelam as páginas como uma forma de preservar o uso de CPU/bateria/dados; elas fazer isso como uma forma de acelerar navegação de avanço e retorno, evitando a necessidade de uma página inteira atualizar.
Possíveis estados anteriores:
Próximos estados possíveis: |
Encerrado |
Uma página fica no estado encerrado assim que começa a ser descarregado e apagado da memória pelo navegador. Não novas tarefas podem começar nesse estado, e as tarefas em andamento podem ser mortos se executarem por muito tempo.
Possíveis estados anteriores:
Próximos estados possíveis: |
Descartada |
A página fica no estado descartada quando é descarregada pelo navegador para economizar recursos. Nenhuma tarefa, callback de evento JavaScript de qualquer tipo pode ser executado nesse estado, já que o descarte normalmente ocorrem devido a restrições de recursos, em que iniciar novos processos é impossível. No estado discarded, a própria guia (incluindo o título da guia e o favicon) geralmente fica visível para o usuário. mesmo que a página não esteja mais disponível.
Possíveis estados anteriores:
Próximos estados possíveis: |
Eventos
Os navegadores distribuem muitos eventos, mas apenas uma pequena parte deles sinaliza uma uma possível mudança no estado do ciclo de vida da página. A tabela a seguir descreve todos os eventos que pertencem ao ciclo de vida e lista para quais estados eles podem passar.
Nome | Detalhes |
---|---|
focus
|
Um elemento DOM recebeu foco.
Observação:um evento
Possíveis estados anteriores:
Possíveis estados atuais: |
blur
|
Um elemento DOM perdeu o foco.
Observação:um evento
Possíveis estados anteriores:
Possíveis estados atuais: |
visibilitychange
|
O campo
O valor |
freeze
*
|
A página acaba de ser congelada. Qualquer um freezable das filas de tarefas da página não serão iniciadas.
Possíveis estados anteriores:
Possíveis estados atuais: |
resume
*
|
O navegador retomou uma página congelada.
Possíveis estados anteriores:
Possíveis estados atuais: |
pageshow
|
Uma entrada de histórico de sessão está sendo acessada. Pode ser um carregamento de página totalmente novo ou uma página extraída da
cache de avanço e retorno. Se a página
foi retirado do cache de avanço e retorno, o evento
A propriedade
Possíveis estados anteriores: |
pagehide
|
Uma entrada do histórico de sessão está sendo extraída. Se o usuário navegar para outra página e o navegador puder adicionar
página atual para a etapa de volta/avanço
cache para ser reutilizado mais tarde, a propriedade
Possíveis estados anteriores:
Possíveis estados atuais: |
beforeunload
|
A janela, o documento e os recursos estão prestes a ser descarregados. O documento continua visível e o evento pode ser cancelado nesta ponto
Importante:o evento
Possíveis estados anteriores:
Possíveis estados atuais: |
unload
|
A página está sendo descarregada.
Aviso:
usar o evento
Possíveis estados anteriores:
Possíveis estados atuais: |
* Indica um novo evento definido pela API Page Lifecycle
Novos recursos adicionados no Chrome 68
O gráfico anterior mostra dois estados que são iniciados pelo sistema, em vez de iniciado pelo usuário: congelado e descartado. Como mencionado anteriormente, hoje os navegadores já congelam e descartam guias ocultas (a seu critério), mas os desenvolvedores não têm como saber quando que isso está acontecendo.
No Chrome 68, os desenvolvedores agora podem observar quando uma guia oculta está congelada e
descongelado ouvindo o evento freeze
e resume
no dia document
.
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
A partir do Chrome 68, o objeto document
agora inclui um
wasDiscarded
no Chrome para computadores (o suporte do Android está sendo acompanhado neste problema). Para determinar se uma página foi descartada enquanto estava em um ambiente
você poderá inspecionar o valor dessa propriedade no tempo de carregamento da página (observação:
páginas descartadas devem ser recarregadas para usá-las novamente).
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
Para orientações sobre o que é importante fazer nos freeze
e no resume
, além de como lidar e se preparar para páginas que estão sendo descartadas, consulte
recomendações do desenvolvedor para cada estado.
As próximas seções oferecem uma visão geral de como esses novos recursos se encaixam estados e eventos atuais da plataforma da Web.
Como observar os estados do ciclo de vida da página no código
No modo ativo, passivo e oculto é possível executar um código JavaScript que determina o estado Estado do ciclo de vida da página das APIs atuais da plataforma da Web.
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
Os estados congelado e terminado na
por outro lado, só podem ser detectados no respectivo listener
(freeze
e pagehide
), como o estado é
mudando.
Como observar mudanças de estado.
Com base na função getState()
definida anteriormente, é possível observar todos os atributos
Mudanças de estado do ciclo de vida com o código a seguir.
// 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);
Esse código faz três coisas:
- Define o estado inicial usando a função
getState()
. - Define uma função que aceita um próximo estado e, se houver uma mudança, registra as mudanças de estado no console.
- Adiciona
capturando
listeners de evento para todos os eventos de ciclo de vida necessários, que, por sua vez, chamam
logStateChange()
, transmitindo o próximo estado.
Uma coisa a observar sobre o código é que todos os listeners de evento são adicionados
para window
, e todas elas passaram
{capture: true}
.
Existem alguns motivos para isso acontecer, entre eles:
- Nem todos os eventos de ciclo de vida da página têm o mesmo destino.
pagehide
epageshow
são disparadas emwindow
.visibilitychange
,freeze
eresume
são disparados emdocument
, efocus
eblur
são disparados na própria respectivos elementos DOM. - A maioria desses eventos não surge, o que significa que é impossível adicionar ouvintes de eventos sem captura a um elemento ancestral comum e observam todos um deles.
- A fase de captura é executada antes das fases de destino ou de balão. Portanto, a adição para garantir que sejam executados antes que outros códigos possam cancelá-los.
Recomendações do desenvolvedor para cada estado
Como desenvolvedores, é importante entender os estados do ciclo de vida da página e saber observá-los no código, porque o tipo de trabalho que você deve (e deve não) estão fazendo depende muito do estado em que sua página está.
Por exemplo, claramente não faz sentido exibir uma notificação temporária para o usuário se a página estiver oculta. Esse exemplo é bastante óbvia, há outras recomendações que não são tão óbvias que valem a pena enumerando.
Estado | Recomendações para desenvolvedores |
---|---|
Active |
O estado ativo é o momento mais crítico para o usuário e, portanto, o momento mais importante para que sua página seja responsivo à entrada do usuário. Qualquer trabalho que não seja da interface e que possa bloquear a linha de execução principal não deve ser priorizado para períodos de inatividade ou transferidos para um worker da Web. |
Passive |
No estado passivo, o usuário não está interagindo com a página. mas ainda podem vê-lo. Isso significa que as atualizações e animações da interface ser suave, mas o momento em que essas atualizações ocorrem é menos importante. Quando a página muda de ativa para passiva, trata-se de uma um bom momento para manter o estado do aplicativo não salvo. |
Quando a página muda de passiva para oculta, é o usuário não interagirá com ele novamente até que seja recarregado. A transição para o estado oculto geralmente também é a última mudança de estado
que podem ser observados com segurança pelos desenvolvedores (isso é especialmente válido para
dispositivos móveis, já que os usuários podem fechar guias ou o próprio navegador, e os
Isso significa que você deve tratar o estado oculto como a extremidade provável para sessão do usuário. Em outras palavras, manter qualquer estado do aplicativo não salvo e enviar dados de análise não enviados. Você também deve parar de fazer atualizações de interface (já que elas não serão vistas pelo usuário), e interrompa todas as tarefas que o usuário não queira executados em segundo plano. |
|
Frozen |
No estado congelado, tarefas congeláveis filas de tarefas ficam suspensas até que a página seja descongelada, o que pode nunca acontecer (por exemplo, se a página for descartada). Isso significa que, quando a página muda de oculta para congelada é essencial parar os timers ou desativar as conexões que, se estiver congelado, poderá afetar outras guias abertas na mesma origem ou afetar o a capacidade do navegador de colocar a página no cache de avanço e retorno. Especificamente, é importante que você:
Você também precisa manter qualquer estado de visualização dinâmica (por exemplo, posição de rolagem).
em uma visualização de lista infinita)
Se a página mudar de congelada para oculta, é possível reabrir quaisquer conexões encerradas ou reiniciar qualquer pesquisa parou quando a página estava inicialmente congelada. |
Terminated |
Geralmente, você não precisa fazer nada quando uma página faz a transição ao estado terminated. Como as páginas que estão sendo descarregadas como resultado de uma ação do usuário sempre aparecem pelo estado hidden antes de entrar no estado terminated o estado oculto é onde a lógica de encerramento de sessão (por exemplo, estado do aplicativo persistente e geração de relatórios para análise) devem ser realizada. Além disso, como mencionado nas recomendações para
o estado oculto), é muito importante que os desenvolvedores percebam
que a transição para o estado encerrado não possa ser confiável
são detectados em muitos casos (especialmente em dispositivos móveis), para que os desenvolvedores
em eventos de encerramento (por exemplo, |
Discarded |
O estado discarded não é observável pelos desenvolvedores no hora que uma página é descartada. Isso ocorre porque as páginas normalmente são descartado por restrições de recursos e descongelar uma página apenas para permitir script a ser executado em resposta a um evento de descarte simplesmente não é possível em na maioria dos casos. Como resultado, você deve se preparar para a possibilidade de descarte em
é possível mudar de oculto para congelado.
reagir à restauração de uma página descartada no tempo de carregamento da página por
verificando |
Como a confiabilidade e a ordem dos eventos do ciclo de vida não são implementadas consistentemente em todos os navegadores, a forma mais fácil de seguir os conselhos na tabela é usar PageLifecycle.js (link em inglês).
APIs de ciclo de vida legadas a serem evitadas
Os eventos a seguir devem ser evitados sempre que possível.
O evento de descarregamento
Muitos desenvolvedores tratam o evento unload
como um callback garantido e o usam como
um sinal de fim de sessão para salvar o estado e enviar dados de análise, mas fazer isso
é extremamente não confiável, especialmente em dispositivos móveis. O evento unload
não
disparar em muitas situações típicas de descarregamento, incluindo o fechamento de uma guia a partir dela
no dispositivo móvel ou fechar o navegador usando o seletor de apps.
Por isso, é sempre melhor confiar no
Evento visibilitychange
para determinar quando uma sessão
e considere o estado oculto que
tempo confiável da última vez para economizar dados do app e do usuário.
Além disso, a mera presença de um manipulador de eventos unload
registrado (via
onunload
ou addEventListener()
) pode impedir que os navegadores possam
colocar páginas no cache de avanço e retorno para acelerar
para frente e para trás.
Em todos os navegadores mais recentes, é recomendável sempre usar a
O evento pagehide
para detectar possíveis descarregamentos de página (também conhecidos como o
estado terminado) em vez do evento unload
. Se você
for compatível com o Internet Explorer versão 10 e inferior, deverá apresentar
detectar o evento pagehide
e usar unload
apenas se o navegador não oferecer suporte
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.
});
O evento beforeunload
O evento beforeunload
tem um problema semelhante ao unload
, por isso,
historicamente, a presença de um evento beforeunload
poderia impedir que as páginas
qualificação para o cache de avanço e retorno. Navegadores modernos
não têm essa restrição. Embora alguns navegadores, por precaução, não disparem
o evento beforeunload
ao tentar colocar uma página em "Voltar/avançar".
o que significa que o evento não é confiável como sinal de fim de sessão.
Além disso, alguns navegadores (incluindo o Chrome)
exigem uma interação do usuário na página antes de permitir o evento beforeunload
.
disparar, o que afeta ainda mais sua confiabilidade.
Uma diferença entre beforeunload
e unload
é que há
usos legítimos de beforeunload
. Por exemplo, quando você quer avisar o usuário
que há alterações não salvas que perderão se continuarem a descarregar a página.
Como há motivos válidos para usar beforeunload
, é recomendável que você
Adicione listeners beforeunload
só quando um usuário tiver mudanças não salvas e, em seguida,
removê-los imediatamente depois de serem salvos.
Em outras palavras, não faça isso, já que adiciona um listener 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);
}
});
Em vez disso, faça isso, já que ele só adiciona o listener beforeunload
quando
necessário e a remove quando não são):
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);
});
Perguntas frequentes
Por que não há um "carregamento" estado?
A API Page Lifecycle define os estados para serem discretos e mutuamente exclusivos. Como uma página pode ser carregada nos estados ativo, passivo ou oculto, e já que pode mudar de estado ou até ser encerrado antes de terminar de carregar, uma estado de carregamento separado não faz sentido nesse paradigma.
Minha página funciona de forma importante quando está oculta. Como posso impedir que ela seja congelada ou descartada?
Existem muitos motivos legítimos para que páginas da Web não sejam congeladas durante a execução no estado oculto. O exemplo mais óbvio é um app que toca música.
Também há situações em que seria arriscado
o Chrome descartar uma página,
por exemplo, se o formulário tiver uma entrada de usuário não enviada ou se houver
Gerenciador beforeunload
que avisa quando a página está sendo descarregada.
Por enquanto, o Chrome será conservador ao descartar páginas e só faça isso quando tiver certeza de que não vai afetar os usuários. Por exemplo, as páginas que foram observados realizando as seguintes ações enquanto estiver no estado oculto, não terão serão descartados, a menos que sob restrições extremas de recursos:
- Reproduzindo áudio
- Como usar o WebRTC
- Atualizar o título ou o favicon da tabela
- Mostrando alertas
- Como enviar notificações push
Para os recursos de lista atuais usados para determinar se uma guia pode ser congelados ou descartados, consulte: Heurística para congelamento e Descartando no Chrome.
O que é o cache de avanço e retorno?
Cache de avanço e retorno é um termo usado para descrever uma otimização da navegação que alguns navegadores implementam, fazendo com que o uso das funções de retorno e avançar mais rapidamente.
Quando o usuário sai de uma página, esses navegadores congelam uma versão da página
para que ela possa ser retomada rapidamente caso o usuário navegue de volta usando
os botões "Voltar" ou "Avançar". Lembre-se de que adicionar um unload
manipulador de eventos impede que essa otimização seja possível.
Para todos os propósitos, esse congelamento é funcionalmente o mesmo que o congelamento dos navegadores funciona para economizar CPU/bateria; por isso é são considerados parte do estado congelado do ciclo de vida.
Se não for possível executar APIs assíncronas nos estados congelado ou encerrado, como salvar dados no IndexedDB?
Nos estados congelado e encerrado, tarefas congeladas nas filas de tarefas de uma página são suspensas, o que significa que APIs assíncronas e baseadas em callback, como IndexedDB, não pode ser usado de maneira confiável.
No futuro, adicionaremos um método commit()
aos objetos IDBTransaction
, o que
oferecem aos desenvolvedores uma maneira de executar o que são, efetivamente, transações somente gravação
que não exigem callbacks. Em outras palavras, se o desenvolvedor está apenas criando
para IndexedDB e não realizam uma transação complexa que consiste em leituras
e gravações, o método commit()
poderá terminar antes que as filas de tarefas sejam
suspenso (supondo que o banco de dados do IndexedDB já esteja aberto).
No entanto, para códigos que precisam funcionar hoje, os desenvolvedores têm duas opções:
- Usar o armazenamento de sessão: armazenamento de sessão é síncrono e persiste entre descartes de páginas.
- Use o IndexedDB do seu service worker: um service worker pode armazenar dados no
IndexedDB após a página ter sido encerrada ou descartada. No
freeze
ou É possível enviar dados ao service worker por meio de um listener de eventospagehide
.postMessage()
, e o service worker pode salvar os dados.
Como testar o app nos estados congelado e descartado
Para testar como o app se comporta nos estados congelado e descartado, acesse
chrome://discards
para congelar ou descartar qualquer uma das suas
guias abertas.
Isso garante que sua página processe corretamente o freeze
e o resume
.
bem como a sinalização document.wasDiscarded
quando as páginas são atualizadas após
um descarte.
Resumo
Desenvolvedores que querem respeitar os recursos do sistema dos dispositivos dos usuários precisam criar apps pensando nos estados do ciclo de vida da página. É fundamental que as páginas da web não estejam consumindo recursos excessivos do sistema em situações que o usuário não espera
Quanto mais desenvolvedores começarem a implementar as novas APIs de ciclo de vida de página, mais seguro ele será. farão com que os navegadores congelem e descartem páginas que não estão sendo usadas. Isso significa que os navegadores vão consumir menos recursos de memória, CPU, bateria e rede, o que é uma vitória para os usuários.