Duas boas práticas de desempenho são definir uma data de expiração futura e um atraso para o carregamento de elementos (especialmente scripts) até após o evento de onload. Mas acontece que a combinação dessas boas práticas leva a uma situação em que é difícil para os usuários renovar os dados. Sendo mais específico, apertar Recarregar (ou mesmo shift+Recarregar) não atualiza esses elementos cacheáveis e “preguiçosos” no Firefox , Chrome, Safari, Android e iPhone.
O que esperamos do botão Recarregar
Um navegador possui um cache (ou 10) no qual ele salva cópias das respostas http. Se o usuário achar que essas respostas cacheadas estão caducas, ele pode apertar o botão Recarregar pra ignorar o cache e buscar tudo novamente e, assim, se assegurar de que ele está vendo a última versão do conteúdo do website. Eu não consegui achar nada na especificação do HTTP que dita o comportamento do botão Recarregar, mas todos os navegadores possuem esse comportamento, até onde eu sei:
- Se você clicar no Recarregar (ou control+R ou command+R), todos os elementos são então buscados novamente utilizando uma requisição GET condicional (com os validadores If-Modified-Since e If-None-Match). Se a resposta da versão do servidor não tiver mudanças, ele retorna um status “304 Not Modified” curto, sem resposta no corpo da mensagem (body). Se a resposta for que houve mudanças, então será “200 OK” e todo o corpo da resposta é enviado.
- Se você clicar em shift+Reload (ou ctrl+Recarregar ou ctrl+shift+R ou command+shift+R) então todos os elementos são buscados novamente SEM os cabeçalhos de validação. Isso é menos eficiente, uma vez que cada resposta do corpo da mensagem é retornada, mas garante que qualquer resposta em cache, que esteja caduca, seja sobrescrita.
Apesar das datas de expiração, nossa expectativa é de que ao apertar Recarregar, tenhamos a última versão do website e com shift+Recarregar faremos isso de forma ainda mais agressiva.
Bem-vindo ao Reload 2.0
Nos tempos de Web 1.0, os elementos eram requisitados utilizando marcação HTML – IMG, SCRIPT, LINK etc. Com a Web 2.0, os elementos são frequentemente requisitados dinamicamente. Dois exemplos comuns são o carregamento de scripts assincronamente (ex.: Google Analytics) e dinamicamente, buscando imagens (ex:. em exibições de fotos e imagens abaixo da primeira “dobra”). Às vezes, esses elementos são requisitados depois que a janela carregou, para que assim a página principal possa renderizar mais rapidamente e oferecer uma experiência melhor ao usuário, melhores métricas etc. Se os elementos possuem uma data de expiração distante, o navegador precisa de uma inteligência extra para fazer a coisa certa.
- Se o usuário navegar pela página normalmente (clicando em um link, digitando uma URL, utilizando um bookmark etc.) e caso o elemento dinâmico esteja em cache, o navegador deve usar a cópia em cache (supondo que a data de expiração ainda está por vir).
- Se o usuário recarregar a página, o navegador deve buscar novamente todos os elementos, incluindo aqueles carregados dinamicamente na página.
- Se o usuário recarregar a página, eu pensaria que os elementos recarregados através da ação devem ser requisitados de novo. Eles provavelmente incluem partes da construção básica da página e devem ser requisitados caso o usuário queira atualizar os conteúdos da página.
- Mas o que o navegador deve fazer se o usuário recarregar a página e há elementos carregados depois do evento de carregamento? Alguns aplicativos web possuem sessões que podem durar por horas ou mesmo dias. Se o usuário não recarregar, deve cada elemento ser carregado dinamicamente por toda a sessão do aplicativo web, ignorando o cache?
Um exemplo
Vamos olhar para um exemplo: Recarregamento após onload.
Essa página carrega imagens e scripts usando cinco diferentes técnicas:
- marcação – a abordagem HTML básica: <img src= e <script src=
- dinamicamente no body – no body da página, há um bloco de script que cria uma imagem e um script de um elemento dinâmico que configura o SRC fazendo com que o elemento seja buscado. Esse código é executado antes do carregamento.
- Onload – Uma imagem e um script são criados dinamicamente pelo evento que causou o carregamento.
- 1ms post-onload – uma imagem e um script são criados dinamicamente chamando a resposta setTimeout de 1 milisegundo no evento causador do carregamento.
- 5 second post-on – uma imagem e um script são criados dinamicamente chamando a resposta setTimeout de de 5 segundos no evento causador do carregamento.
Todas as imagens e os scripts têm uma data de expiração de um mês no futuro. Se o usuário clicar em Recarregar, quais das técnicas devem ser usada para isso? Certamente esperamos que as técnicas 1 e 2 resultem na renovação dos elementos. E até gostaria de ver o número 3 fazer o mesmo. Creio que a técnica 4 deveria funcionar, mas duvido que algum navegador faça isso, e o número 5 provavelmente não deverá causar coisa alguma. Estabeleça as suas expectativas e então dê uma olhada na tabela abaixo.
Os resultados
Antes de irmos direto aos resultados dos testes de recarregamento, vamos dar uma olhada no que acontece se o usuário apenas navegar na página. Isso é alcançado ao clicar no link “try again” do exemplo. Nesse caso, nenhum dos elementos é renovado. Todos os elementos foram salvos em cache com uma data de expiração de um mês à frente, portanto todos os navegadores que testei leem a página a partir do cache. Isso é bom e é o que esperamos que aconteça.
Mas o comportamento diverge quando olhamos para os resultados de carregamento capturados na tabela abaixo.
Tabela 1. elementos renovados ao recarregar
Técnica | elemento | Chrome 25 | Safari 6 | Android | Safari/543iPhone | Firefox 19 | IE 8, 10 | Opera 12 |
marcação | Imagem 1 | S | S | S | S | S | S | S |
Script 1 | S | S | S | S | S | S | S | |
dinamicamente | Imagem 2 | S | S | S | S | S | S | S |
Script 2 | S | S | S | S | S | S | S | |
onload | Imagem 3 | – | – | – | – | S | S | S |
Script 3 | – | – | – | – | – | S | S | |
1ms após onload | Imagem 4 | – | – | – | – | – | – | S |
Script 4 | – | – | – | – | – | – | S | |
5seg após onload | Imagem 5 | – | – | – | – | – | – | – |
Script 5 | – | – | – | – | – | – | – |
Os resultados para Chrome, Safari, Android mobile e Safari mobile são os mesmos. Quando você clica em Recarregar nesses navegadores, os elementos da página não buscados novamente (elementos 1 e 2), mas não os elementos carregados após o manipulador onload.
O Firefox é interessante. Ele carrega os quatro elementos da página, mais a imagem do manipulador onload, mas não o script (script 3). Curioso.
O IE 8 e 10 são iguais: eles carregam os quatro elementos na página e também a imagem e o script do manipulador onload (elementos 1-3). Não testei o IE 9, mas acredito que seja igual.
O Opera teve os melhores resultados, na minha opinião. Ele renovou todos os elementos da página principal, o manipulador onload e aquele 1 milissegundo depois do onload (elementos 1-4), mas não renovou os elementos 5 segundos depois do onload (imagem 5 e script 5). E faço uma provocação quanto a isso. Se eu aumentar o tempo de 1 milissegundo para 50 milissegundos, então a imagem e o script 4 não são recarregados. Acho que isso seja uma condição temporal em que o Opera ainda pode estar baixando os elementos do manipulador onload quando esses primeiros elementos são criados e são então igualmente renovados. Para verificar isso mais a fundo, eu aumentei o atraso para 500 milissegundos e confirmei que os elementos não foram renovados, mas o tempo de resposta de todos os elementos aumentou para 1 segundo (antes era instantâneo) e isso fez com que a imagens e o script 4 fossem recarregados, mesmo o atraso sendo de 500 milissegundos depois do onload.
Note que pressionar shift+Recarregar (e outras combinações) não altera os resultados.
Para viagem
Um tanto quanto esotérico? Talvez. Esse é um mergulho em um tema bem específico. Eu garanto isso. Mas eu tenho alguns poréns:
Se você é um desenvolvedor utilizando datas de expiração bem “para frente” e lazy loading, você talvez obtenha resultados inesperados quando alterar um elemento e clicar em Recarregar, mesmo com shift+Reload. Se você não está vendo a versão mais recente de seus elementos, talvez seja preciso limpar o seu cache.
Isso não é uma questão apenas para desenvolvedores web. Isso afeta usuários também. Muitos sites utilizam elementos lazy loading e datas de expiração futuras, incluindo 8 entre os 10 maiores: Google, YouTube, Yahoo!, Microsoft Live, Tencent QQ, Amazon e Twitter. Se você recarregar qualquer um com um sniffer de pacotes aberto em um dos quatro navegadores listados, irá ver um padrão curioso: elementos cacheáveis carregados antes que o onload tivesse uma resposta de status 304, enquanto outros, após o onload, são lidos a partir do cache e não são renovados. A única forma de se certificar de que possui uma versão recente é limpar o cache, acabando com as vantagens do botão de recarregar.
Aqui temos um gráfico exibindo as requisições feitas quando o site da Amazon é recarregado no Chrome. A linha vertical vermelha marca o evento de onload. Perceba como os elementos anteriores ao onload recebem códigos de status 304. Logo após o onload há algumas imagens que não são cacheáveis, portanto são renovadas e retornam código 200. As imagens carregadas após o onload são todas lidas a partir do cache, portanto qualquer atualização desses elementos é perdida.
Finalmente, mesmo que o comportamento dos diferentes navegadores variem, vale a pena investigar o porquê. Nesse caso, deveríamos tornar o recarregamento mais consistente e fazê-lo renovar os elementos, mesmo aqueles carregados dinamicamente no manipulador onload.
***
Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.stevesouders.com/blog/2013/02/26/reloading-post-onload-resources/