Neste artigo, irei compartilhar 8 técnicas para otimizar o carregamento de imagens que reduzem a largura de banda da rede necessária e a carga do processador quando exibidas na tela. Aqui estão alguns exemplos de HTML com anotações para facilitar a sua reprodução. Algumas técnicas são conhecidas há muito tempo e outras surgiram há relativamente pouco tempo. Idealmente, seu mecanismo de publicação de documento da Web favorito (como um CMS, gerador de site estático ou estrutura de aplicativo da Web) deve fazer tudo isso imediatamente.
Coletivamente, as técnicas otimizam todos os elementos do Google Core Web Vitals por:
- minimizar os principais problemas de conteúdo ( Largest Contentful Paint (LCP) ) por meio da redução de tamanho, armazenamento em cache e carregamento lento;
- preservação de mudança de layout cumulativa zero ( Mudança de layout cumulativa (CLS) );
- reduzindo o atraso da primeira entrada ( First Input Delay (FID) ), reduzindo o consumo do processador (para o thread principal de execução).
Para ver todas as técnicas em ação, dê uma olhada no código-fonte para carregar esta imagem:
https://www.industrialempathy.com/img/remote/ZiClJf.jpg
<img loading="lazy" decoding="async" style="background-size: cover; background-image: none;" src="/img/remote/ZiClJf.avif" alt="Sample image illustrating the techniques outlined in this post." width="4032" height="2268">
Técnicas de otimização
Layout responsivo
Essa técnica simples permite que a imagem ocupe o espaço horizontal disponível, mantendo a proporção da imagem. Em 2020, os navegadores aprenderam a reservar a quantidade correta de espaço vertical para uma imagem antes de carregar se o elemento
img
contiver atributos
width
e
height
. Isso evita a mudança cumulativa do layout.
<style>
img {
max-width: 100%;
height: auto;
}
</style>
<!-- Providing width and height is more important than ever. -->
<img height="853" width="1280" … />
Renderização preguiçosa
A segunda técnica é mais complicada. O novo atributo CSS
content-visibility: auto
diz ao navegador para não pensar em colocar a imagem até que esteja pronta. Essa abordagem tem várias vantagens, a principal delas é que até que o navegador receba uma imagem desfocada de espaço reservado ou a própria imagem, ele não a decodificará, economizando recursos do processador.
Não é mais necessário conter tamanho intrínseco
Uma versão anterior do artigo explicou como
contain-intrinsic-size
evitar o efeito CLS ao usar
content-visibility: auto
. Mas no Chromium 88 isso não é mais necessário no caso de imagens para as quais
width
e
height
. A partir de 27 de janeiro de 2021,
content-visibility: auto
ainda não implementados em outros motores de navegador , eles provavelmente seguirão o exemplo do Chromium. Então, sim, é muito mais fácil agora!
<style>
/* This probably only makes sense for images within the main scrollable area of your page. */
main img {
/* Only render when in viewport */
content-visibility: auto;
}
</style>
AVIF
AVIF é o formato gráfico mais recente que recebeu suporte em navegadores. Agora é compatível com Chromium e por sinalização no Firefox. O Safari ainda não funciona com ele, mas como a Apple faz parte do grupo que desenvolveu o formato, este navegador também deverá suportar AVIF no futuro.
Este formato é notável por ser muito superior ao JPEG. E isso se compara favoravelmente com o formato WebP, cujas imagens nem sempre são menores que JPEG e que podem aumentar o consumo de recursos devido à falta de suporte para carregamento progressivo.
Para implementar uma extensão progressiva para AVIF, você pode usar
picture
.
O elemento está realmente
img
aninhado em
picture
... Isso pode ser confuso, porque
img
às vezes é chamado de solução alternativa para navegadores que não oferecem suporte
picture
, mas na verdade esse elemento apenas ajuda na seleção
src
e não tem seu próprio layout. O elemento
img
será desenhado e você aplicará o estilo a ele.
Até recentemente, era muito difícil implementar imagens AVIF no lado do servidor, mas versões recentes de bibliotecas como o Sharp tornaram essa tarefa muito mais fácil.
<picture>
<source
sizes="(max-width: 608px) 100vw, 608px"
srcset="
/img/Z1s3TKV-1920w.avif 1920w,
/img/Z1s3TKV-1280w.avif 1280w,
/img/Z1s3TKV-640w.avif 640w,
/img/Z1s3TKV-320w.avif 320w
"
type="image/avif"
/>
<!-- snip lots of other stuff -->
<img />
</picture>
Carregando o número correto de pixels
O código acima possui atributos
srcset
e
sizes
. Eles usam um seletor
w
para informar ao navegador qual URL usar com base no número físico de pixels necessários para renderizar a imagem em um determinado dispositivo. Esse valor depende da largura da imagem, que é calculada com base no atributo
sizes
(que é uma expressão da consulta de mídia).
Isso garante que o navegador sempre carregue a menor imagem possível, fornecendo a melhor qualidade em um determinado dispositivo. Como alternativa, ele pode selecionar a menor imagem se o usuário habilitou o modo de salvamento de dados.
Solução alternativa
Para navegadores que suportam apenas formatos de imagem mais antigos, você pode
srcset
fornecer mais elementos brutos com a ajuda de:
<source
sizes="(max-width: 608px) 100vw, 608px"
srcset="
/img/Z1s3TKV-1920w.webp 1920w,
/img/Z1s3TKV-1280w.webp 1280w,
/img/Z1s3TKV-640w.webp 640w,
/img/Z1s3TKV-320w.webp 320w
"
type="image/webp"
/>
<source
sizes="(max-width: 608px) 100vw, 608px"
srcset="
/img/Z1s3TKV-1920w.jpg 1920w,
/img/Z1s3TKV-1280w.jpg 1280w,
/img/Z1s3TKV-640w.jpg 640w,
/img/Z1s3TKV-320w.jpg 320w
"
type="image/jpeg"
/>
Cache e URLs imutáveis
Incorpore no URL da imagem um hash do número de bytes que a imagem ocupa. No exemplo acima, eu fiz isso com
Z1s3TKV
. Quando você altera a imagem, o URL também muda, o que significa que você pode aplicar o cache infinito de imagens. Os cabeçalhos de cache devem ser semelhantes
cache-control: public,max-age=31536000,immutable
.
immutable
É um significado semanticamente correto
cache-control
, mas tem pouco suporte a navegadores hoje (estou olhando para você, Chrome).
max-age=31536000
- método de armazenamento em cache substituto ao longo do ano.
public
é necessário para o seu CDN armazenar a imagem em cache e entregá-la da borda da rede. Mas essa abordagem só pode ser usada se não violar suas políticas de privacidade.
Carregamento lento
Ao adicionar
loading=«lazy»
ao elemento,
img
dizemos ao navegador para começar a buscar a imagem apenas quando ela estiver pronta para ser renderizada.
<img loading="lazy" … />
Descriptografia assíncrona
Ao adicionar
decoding=«async»
ao elemento,
img
permitimos que o navegador descriptografe a imagem fora do stream principal para que esse procedimento não interfira com o usuário. Não deve haver nenhuma falha perceptível nesta solução, exceto que nem sempre é aplicável por padrão em navegadores mais antigos.
<img decoding="async" … />
Esboço borrado
Um stub difuso é uma imagem embutida que dá ao usuário uma ideia de uma imagem completa que será carregada posteriormente, sem transferir dados pela rede.
https://www.industrialempathy.com/img/blurry.svg
Algumas notas de implementação:
- O esboço é embutido como
background-image
imagens. Essa técnica permite eliminar o segundo elemento HTML, ocultando literalmente o stub quando a imagem principal é carregada, sem a necessidade de JavaScript. - O URI dos dados da imagem principal é agrupado no URI dos dados da imagem SVG. Isso é feito porque o desfoque é feito no nível SVG e não usando um filtro CSS. Ou seja, o desfoque é feito uma vez para cada imagem quando rasterizada por SVG, não para cada layout. Isso economiza recursos do processador.
<img
style="
…
background-size: cover;
background-image:
url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns=\'http%3A//www.w3.org/2000/svg\'
xmlns%3Axlink=\'http%3A//www.w3.org/1999/xlink\' viewBox=\'0 0 1280 853\'%3E%3Cfilter id=\'b\' color-interpolation-filters=\'sRGB\'%3E%3CfeGaussianBlur stdDeviation=\'.5\'%3E%3C/feGaussianBlur%3E%3CfeComponentTransfer%3E%3CfeFuncA type=\'discrete\' tableValues=\'1 1\'%3E%3C/feFuncA%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter=\'url(%23b)\' x=\'0\' y=\'0\' height=\'100%25\' width=\'100%25\'
xlink%3Ahref=\'data%3Aimage/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAIAAACepSOSAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAs0lEQVQI1wGoAFf/AImSoJSer5yjs52ktp2luJuluKOpuJefsoCNowB+kKaOm66grL+krsCnsMGrt8m1u8mzt8OVoLIAhJqzjZ2tnLLLnLHJp7fNmpyjqbPCqLrRjqO7AIeUn5ultaWtt56msaSnroZyY4mBgLq7wY6TmwCRfk2Pf1uzm2WulV+xmV6rmGyQfFm3nWSBcEIAfm46jX1FkH5Djn5AmodGo49MopBLlIRBfG8yj/dfjF5frTUAAAAASUVORK5CYII=\'%3E%3C/image%3E%3C/svg%3E');
"
…
/>
(Opcional) Otimização de JavaScript
Os navegadores podem ser forçados a rasterizar o esboço desfocado, mesmo se a imagem já estiver carregada. O problema pode ser resolvido removendo a rasterização na inicialização. Além disso, se sua imagem tiver áreas transparentes, essa otimização se torna obrigatória, caso contrário, um esboço aparecerá na imagem.
<sript>
document.body.addEventListener(
"load",
(e) => {
if (e.target.tagName != "IMG") {
return;
}
// Remove the blurry placeholder.
e.target.style.backgroundImage = "none";
},
/* capture */ true
);
</sript>
Além disso
Uma ferramenta útil que implementa todas as otimizações descritas: eleventy-high-performance-blog