Lista de verificação de desempenho de front-end 2021 (PDF, Apple Pages, MS Word)

Publicados: 2022-03-10
Resumo rápido ↬ Vamos fazer 2021… rápido! Uma lista de verificação anual de desempenho de front-end (disponível como PDF, Apple Pages, MS Word), com tudo o que você precisa saber para criar experiências rápidas na Web hoje, de métricas a ferramentas e técnicas de front-end. Atualizado desde 2016. Ah, você também pode obter dicas úteis de front-end em nosso boletim informativo por e-mail.

O desempenho da Web é uma fera complicada, não é? Como realmente sabemos onde estamos em termos de desempenho e quais são exatamente nossos gargalos de desempenho? É JavaScript caro, entrega lenta de fontes da Web, imagens pesadas ou renderização lenta? Otimizamos o suficiente com trepidação de árvores, elevação de escopo, divisão de código e todos os padrões de carregamento sofisticados com observador de interseção, hidratação progressiva, dicas de clientes, HTTP/3, service workers e — oh meu — edge workers? E, mais importante, por onde começamos a melhorar o desempenho e como estabelecemos uma cultura de desempenho a longo prazo?

Antigamente, o desempenho era muitas vezes uma mera reflexão tardia . Muitas vezes adiado até o final do projeto, se resumiria a minificação, concatenação, otimização de ativos e potencialmente alguns ajustes finos no arquivo de config do servidor. Olhando para trás agora, as coisas parecem ter mudado bastante.

O desempenho não é apenas uma preocupação técnica: afeta tudo, desde a acessibilidade à usabilidade até a otimização do mecanismo de pesquisa e, ao incluí-lo no fluxo de trabalho, as decisões de design precisam ser informadas por suas implicações de desempenho. O desempenho deve ser medido, monitorado e refinado continuamente , e a crescente complexidade da web apresenta novos desafios que dificultam o acompanhamento das métricas, pois os dados variam significativamente dependendo do dispositivo, navegador, protocolo, tipo de rede e latência ( CDNs, ISPs, caches, proxies, firewalls, balanceadores de carga e servidores desempenham um papel no desempenho).

Então, se criássemos uma visão geral de todas as coisas que devemos ter em mente ao melhorar o desempenho – desde o início do projeto até o lançamento final do site – como seria? Abaixo, você encontrará uma lista de verificação de desempenho de front-end (espero que imparcial e objetiva) para 2021 - uma visão geral atualizada dos problemas que você pode precisar considerar para garantir que seus tempos de resposta sejam rápidos, a interação do usuário seja suave e seus sites não drenar a largura de banda do usuário.

Índice

  • Tudo em páginas separadas
  • Preparação: planejamento e métricas
    Cultura de desempenho, Core Web Vitals, perfis de desempenho, CrUX, Lighthouse, FID, TTI, CLS, dispositivos.
  • Definir metas realistas
    Orçamentos de desempenho, metas de desempenho, estrutura RAIL, orçamentos de 170 KB/30 KB.
  • Definindo o Ambiente
    Escolhendo uma estrutura, custo de desempenho de linha de base, Webpack, dependências, CDN, arquitetura front-end, CSR, SSR, CSR + SSR, renderização estática, pré-renderização, padrão PRPL.
  • Otimizações de recursos
    Brotli, AVIF, WebP, imagens responsivas, AV1, carregamento de mídia adaptável, compactação de vídeo, fontes da web, fontes do Google.
  • Otimizações de compilação
    Módulos JavaScript, padrão de módulo/nomódulo, trepidação de árvore, divisão de código, elevação de escopo, Webpack, serviço diferencial, web worker, WebAssembly, pacotes JavaScript, React, SPA, hidratação parcial, importação na interação, terceiros, cache.
  • Otimizações de entrega
    Carregamento lento, observador de interseção, renderização e decodificação de adiamento, CSS crítico, streaming, dicas de recursos, mudanças de layout, service worker.
  • Rede, HTTP/2, HTTP/3
    Grampeamento OCSP, certificados EV/DV, embalagem, IPv6, QUIC, HTTP/3.
  • Teste e monitoramento
    Fluxo de trabalho de auditoria, navegadores proxy, página 404, prompts de consentimento de cookies GDPR, CSS de diagnóstico de desempenho, acessibilidade.
  • Vitórias rápidas
  • Baixe a lista de verificação (PDF, Apple Pages, MS Word)
  • Lá vamos nós!

(Você também pode baixar o PDF da lista de verificação (166 KB) ou baixar o arquivo editável do Apple Pages (275 KB) ou o arquivo .docx (151 KB). Boa otimização, pessoal!)

Preparação: planejamento e métricas

As micro-otimizações são ótimas para manter o desempenho sob controle, mas é fundamental ter em mente metas claramente definidas — metas mensuráveis ​​que influenciariam quaisquer decisões tomadas ao longo do processo. Existem alguns modelos diferentes, e os discutidos abaixo são bastante opinativos – apenas certifique-se de definir suas próprias prioridades desde o início.

  1. Estabeleça uma cultura de desempenho.
    Em muitas organizações, os desenvolvedores front-end sabem exatamente quais são os problemas subjacentes comuns e quais estratégias devem ser usadas para corrigi-los. No entanto, enquanto não houver um endosso estabelecido da cultura de desempenho, cada decisão se transformará em um campo de batalha de departamentos, dividindo a organização em silos. Você precisa da adesão de uma parte interessada nos negócios e, para obtê-la, precisa estabelecer um estudo de caso ou uma prova de conceito sobre como a velocidade - especialmente os principais aspectos vitais da Web , que abordaremos em detalhes mais adiante - beneficia as métricas e os indicadores-chave de desempenho ( KPIs ) com os quais eles se importam.

    Por exemplo, para tornar o desempenho mais tangível, você pode expor o impacto no desempenho da receita mostrando a correlação entre a taxa de conversão e o tempo de carregamento do aplicativo, bem como o desempenho de renderização. Ou a taxa de rastreamento do bot de pesquisa (PDF, páginas 27–50).

    Sem um forte alinhamento entre as equipes de desenvolvimento/design e de negócios/marketing, o desempenho não se sustentará a longo prazo. Estude reclamações comuns que chegam ao atendimento ao cliente e à equipe de vendas, estude análises para altas taxas de rejeição e quedas de conversão. Explore como melhorar o desempenho pode ajudar a aliviar alguns desses problemas comuns. Ajuste o argumento dependendo do grupo de stakeholders com quem você está falando.

    Execute experimentos de desempenho e meça os resultados — tanto em dispositivos móveis quanto em computadores (por exemplo, com o Google Analytics). Ele irá ajudá-lo a construir um estudo de caso sob medida para a empresa com dados reais. Além disso, o uso de dados de estudos de caso e experimentos publicados no WPO Stats ajudará a aumentar a sensibilidade dos negócios sobre por que o desempenho é importante e qual o impacto que ele tem na experiência do usuário e nas métricas de negócios. Afirmar que o desempenho importa por si só não é suficiente - você também precisa estabelecer algumas metas mensuráveis ​​e rastreáveis ​​e observá-las ao longo do tempo.

    Como chegar lá? Em sua palestra sobre Construindo Desempenho a Longo Prazo, Allison McKnight compartilha um estudo de caso abrangente de como ela ajudou a estabelecer uma cultura de desempenho no Etsy (slides). Mais recentemente, Tammy Everts falou sobre hábitos de equipes de desempenho altamente eficazes em organizações pequenas e grandes.

    Ao ter essas conversas nas organizações, é importante ter em mente que, assim como o UX é um espectro de experiências, o desempenho na web é uma distribuição. Como Karolina Szczur observou, "esperar que um único número seja capaz de fornecer uma classificação a que aspirar é uma suposição falha". Portanto, as metas de desempenho precisam ser granulares, rastreáveis ​​e tangíveis.

No celular, por sessão, os usuários que experimentaram tempos de carregamento rápidos geram 17% mais receita do que a média
No celular, por sessão, os usuários que experimentaram tempos de carregamento rápidos geram 17% mais receita do que a média. (Impacto do desempenho na Web, via Addy Osmani)
Esperar que um único número seja capaz de fornecer uma classificação a que aspirar é uma suposição falha
Esperar que um único número seja capaz de fornecer uma classificação à qual aspirar é uma suposição equivocada. (Crédito da imagem: Performance é uma distribuição via Karolina Czczur)
  1. Objetivo: Ser pelo menos 20% mais rápido que seu concorrente mais rápido.
    De acordo com pesquisas psicológicas, se você quer que os usuários sintam que seu site é mais rápido que o site do seu concorrente, você precisa ser pelo menos 20% mais rápido. Estude seus principais concorrentes, colete métricas sobre o desempenho deles em dispositivos móveis e desktops e defina limites que o ajudariam a superá-los. No entanto, para obter resultados e metas precisos, certifique-se de obter primeiro uma imagem completa da experiência de seus usuários estudando suas análises. Você pode então imitar a experiência do 90º percentil para teste.

    Para obter uma boa primeira impressão do desempenho de seus concorrentes, você pode usar o Chrome UX Report ( CrUX , um conjunto de dados RUM pronto, introdução em vídeo de Ilya Grigorik e guia detalhado de Rick Viscomi) ou Treo, uma ferramenta de monitoramento de RUM que é desenvolvido pelo Chrome UX Report. Os dados são coletados dos usuários do navegador Chrome, portanto, os relatórios serão específicos do Chrome, mas fornecerão uma distribuição bastante completa do desempenho, principalmente as pontuações do Core Web Vitals, em uma ampla variedade de visitantes. Observe que novos conjuntos de dados CrUX são lançados na segunda terça-feira de cada mês .

    Alternativamente, você também pode usar:

    • Ferramenta de comparação de relatórios de UX do Chrome de Addy Osmani,
    • Speed ​​Scorecard (também fornece um estimador de impacto de receita),
    • Comparação real do teste de experiência do usuário ou
    • SiteSpeed ​​CI (baseado em testes sintéticos).

    Observação : se você usar o Page Speed ​​Insights ou a API do Page Speed ​​Insights (não, ele não está obsoleto!), poderá obter dados de desempenho do CrUX para páginas específicas em vez de apenas os agregados. Esses dados podem ser muito mais úteis para definir metas de desempenho para recursos como "página de destino" ou "lista de produtos". E se você estiver usando CI para testar os orçamentos, você precisa ter certeza de que seu ambiente testado corresponde ao CrUX se você usou o CrUX para definir o destino ( obrigado Patrick Meenan! ).

    Se você precisar de ajuda para mostrar o raciocínio por trás da priorização da velocidade, ou quiser visualizar o declínio da taxa de conversão ou o aumento da taxa de rejeição com desempenho mais lento, ou talvez precise defender uma solução RUM em sua organização, Sergey Chernyshev construiu uma UX Speed ​​Calculator, uma ferramenta de código aberto que ajuda você a simular dados e visualizá-los para direcionar seu ponto de vista.

    O CrUX gera uma visão geral das distribuições de desempenho ao longo do tempo, com o tráfego coletado dos usuários do Google Chrome
    O CrUX gera uma visão geral das distribuições de desempenho ao longo do tempo, com o tráfego coletado dos usuários do Google Chrome. Você pode criar o seu próprio no Chrome UX Dashboard. (Visualização grande)
    Exatamente quando você precisa defender o desempenho para impulsionar seu ponto de vista: o UX Speed ​​Calculator visualiza um impacto do desempenho nas taxas de rejeição, conversão e receita total, com base em dados reais
    Exatamente quando você precisa defender o desempenho para impulsionar seu ponto de vista: o UX Speed ​​Calculator visualiza um impacto do desempenho nas taxas de rejeição, conversão e receita total - com base em dados reais. (Visualização grande)

    Às vezes, você pode querer ir um pouco mais fundo, combinando os dados provenientes do CrUX com quaisquer outros dados que você já tenha para descobrir rapidamente onde estão as lentidão, pontos cegos e ineficiências - para seus concorrentes ou para seu projeto. Em seu trabalho, Harry Roberts tem usado uma Planilha de Topografia de Velocidade de Site que ele usa para dividir o desempenho por tipos de página principais e rastrear como as métricas principais são diferentes entre elas. Você pode baixar a planilha como Planilhas Google, Excel, documento OpenOffice ou CSV.

    Topografia de velocidade do site, com as principais métricas representadas para as principais páginas do site
    Topografia de velocidade do site, com as principais métricas representadas para as principais páginas do site. (Visualização grande)

    E se você quiser ir até o fim, pode executar uma auditoria de desempenho do Lighthouse em todas as páginas de um site (via Lightouse Parade), com uma saída salva como CSV. Isso ajudará você a identificar quais páginas específicas (ou tipos de páginas) de seus concorrentes têm um desempenho pior ou melhor e em que você pode concentrar seus esforços. (Para seu próprio site, provavelmente é melhor enviar dados para um endpoint de análise!).

    Com o Lighthouse Parade, você pode executar uma auditoria de desempenho do Lighthouse em todas as páginas de um site, com uma saída salva como CSV
    Com o Lighthouse Parade, você pode executar uma auditoria de desempenho do Lighthouse em todas as páginas de um site, com uma saída salva como CSV. (Visualização grande)

    Colete dados, monte uma planilha, reduza 20% e defina suas metas ( orçamentos de desempenho ) dessa maneira. Agora você tem algo mensurável para testar. Se você está mantendo o orçamento em mente e tentando enviar apenas a carga útil mínima para obter um tempo rápido de interação, então você está no caminho razoável.

    Precisa de recursos para começar?

    • Addy Osmani escreveu um artigo muito detalhado sobre como iniciar o orçamento de desempenho, como quantificar o impacto de novos recursos e por onde começar quando estiver acima do orçamento.
    • O guia de Lara Hogan sobre como abordar projetos com um orçamento de desempenho pode fornecer dicas úteis para designers.
    • Harry Roberts publicou um guia sobre como configurar uma planilha do Google para exibir o impacto de scripts de terceiros no desempenho, usando o Request Map,
    • A calculadora de orçamento de desempenho de Jonathan Fielding, a calculadora de orçamento de desempenho de Katie Hempenius e as calorias do navegador podem ajudar na criação de orçamentos (graças a Karolina Szczur pelo aviso).
    • Em muitas empresas, os orçamentos de desempenho não devem ser aspiracionais, mas sim pragmáticos, servindo como um sinal de espera para evitar passar de um certo ponto. Nesse caso, você pode escolher seu pior ponto de dados nas últimas duas semanas como um limite e a partir daí. Orçamentos de desempenho, pragmaticamente mostra uma estratégia para conseguir isso.
    • Além disso, torne o orçamento de desempenho e o desempenho atual visíveis configurando painéis com tamanhos de compilação de relatórios de gráficos. Existem muitas ferramentas que permitem que você faça isso: o painel SiteSpeed.io (código aberto), SpeedCurve e Caliber são apenas algumas delas, e você pode encontrar mais ferramentas em perf.rocks.
    As calorias do navegador ajudam você a definir um orçamento de desempenho e medir se uma página está excedendo esses números ou não,
    As calorias do navegador ajudam você a definir um orçamento de desempenho e medir se uma página está excedendo esses números ou não. (Visualização grande)

    Depois de ter um orçamento definido, incorpore-os em seu processo de criação com Webpack Performance Hints and Bundlesize, Lighthouse CI, PWMetrics ou Sitespeed CI para impor orçamentos em pull requests e fornecer um histórico de pontuação nos comentários de PR.

    Para expor orçamentos de desempenho para toda a equipe, integre orçamentos de desempenho no Lighthouse por meio do Lightwallet ou use o LHCI Action para integração rápida do Github Actions. E se precisar de algo personalizado, você pode usar webpagetest-charts-api, uma API de endpoints para construir gráficos a partir dos resultados do WebPagetest.

    No entanto, a conscientização sobre o desempenho não deve vir apenas dos orçamentos de desempenho. Assim como o Pinterest, você pode criar uma regra eslint personalizada que não permita a importação de arquivos e diretórios que são conhecidos por serem muito dependentes e sobrecarregariam o pacote. Configure uma lista de pacotes “seguros” que podem ser compartilhados com toda a equipe.

    Além disso, pense nas tarefas críticas do cliente que são mais benéficas para o seu negócio. Estude, discuta e defina limites de tempo aceitáveis ​​para ações críticas e estabeleça marcas de tempo de usuário "UX ready" que toda a organização aprovou. Em muitos casos, as jornadas do usuário afetarão o trabalho de muitos departamentos diferentes, portanto, o alinhamento em termos de tempos aceitáveis ​​ajudará a apoiar ou evitar discussões de desempenho no futuro. Certifique-se de que os custos adicionais de recursos e recursos adicionados sejam visíveis e compreendidos.

    Alinhe os esforços de desempenho com outras iniciativas de tecnologia, desde novos recursos do produto que está sendo criado até a refatoração para alcançar novos públicos globais. Então, toda vez que uma conversa sobre desenvolvimento adicional acontece, o desempenho também faz parte dessa conversa. É muito mais fácil atingir metas de desempenho quando a base de código é nova ou está apenas sendo refatorada.

    Além disso, como sugeriu Patrick Meenan, vale a pena planejar uma sequência de carregamento e compensações durante o processo de design. Se você priorizar antecipadamente quais partes são mais críticas e definir a ordem em que elas devem aparecer, também saberá o que pode ser atrasado. Idealmente, essa ordem também refletirá a sequência de suas importações de CSS e JavaScript, portanto, será mais fácil manuseá-las durante o processo de compilação. Além disso, considere qual deve ser a experiência visual nos estados "intermediários", enquanto a página está sendo carregada (por exemplo, quando as fontes da Web ainda não foram carregadas).

    Depois de estabelecer uma forte cultura de desempenho em sua organização, tente ser 20% mais rápido do que seu antigo eu para manter as prioridades intactas com o passar do tempo ( obrigado, Guy Podjarny! ). Mas considere os diferentes tipos e comportamentos de uso de seus clientes (que Tobias Baldauf chamou de cadência e coortes), juntamente com o tráfego de bots e efeitos de sazonalidade.

    Planejamento, planejamento, planejamento. Pode ser tentador entrar em algumas otimizações rápidas de "frutos fáceis" no início - e pode ser uma boa estratégia para ganhos rápidos - mas será muito difícil manter o desempenho como prioridade sem planejamento e definição de empresas realistas. - metas de desempenho personalizadas.

O Treo Sites fornece análise competitiva com base em dados do mundo real
O Treo fornece análise competitiva com base em dados do mundo real. (Visualização grande)
Novas métricas chegaram ao Lighthouse v6 no início de 2020
Novas métricas chegaram ao Lighthouse v6 no início de 2020. (Visualização grande)
  1. Escolha as métricas certas.
    Nem todas as métricas são igualmente importantes. Estude quais métricas são mais importantes para seu aplicativo: geralmente, elas serão definidas pela rapidez com que você pode começar a renderizar os pixels mais importantes da sua interface e com que rapidez você pode fornecer resposta de entrada para esses pixels renderizados. Esse conhecimento lhe dará a melhor meta de otimização para esforços contínuos. No final, não são os eventos de carregamento ou os tempos de resposta do servidor que definem a experiência, mas a percepção de quão ágil a interface parece .

    O que isto significa? Em vez de se concentrar no tempo de carregamento da página inteira (por meio de horários onLoad e DOMContentLoaded , por exemplo), priorize o carregamento da página conforme percebido por seus clientes. Isso significa focar em um conjunto ligeiramente diferente de métricas. Na verdade, escolher a métrica certa é um processo sem vencedores óbvios.

    Com base na pesquisa de Tim Kadlec e nas notas de Marcos Iglesias em sua palestra, as métricas tradicionais podem ser agrupadas em alguns conjuntos. Normalmente, precisaremos de todos eles para obter uma visão completa do desempenho e, no seu caso particular, alguns deles serão mais importantes do que outros.

    • Métricas baseadas em quantidade medem o número de solicitações, peso e pontuação de desempenho. Bom para disparar alarmes e monitorar mudanças ao longo do tempo, não tão bom para entender a experiência do usuário.
    • As métricas de marco usam estados no tempo de vida do processo de carregamento, por exemplo, Tempo até o primeiro byte e Tempo até a interação . Bom para descrever a experiência do usuário e monitoramento, não tão bom para saber o que acontece entre os marcos.
    • As métricas de renderização fornecem uma estimativa de quão rápido o conteúdo é renderizado (por exemplo, tempo de renderização inicial , índice de velocidade ). Bom para medir e ajustar o desempenho de renderização, mas não tão bom para medir quando um conteúdo importante aparece e pode interagir.
    • Métricas personalizadas medem um evento específico e personalizado para o usuário, por exemplo, Time To First Tweet do Twitter e PinnerWaitTime do Pinterest. Bom para descrever a experiência do usuário com precisão, não tão bom para dimensionar as métricas e comparar com os concorrentes.

    Para completar o quadro, normalmente procuramos métricas úteis entre todos esses grupos. Normalmente, os mais específicos e relevantes são:

    • Tempo para Interação (TTI)
      O ponto em que o layout se estabilizou , as principais fontes da web ficam visíveis e o encadeamento principal está disponível o suficiente para lidar com a entrada do usuário - basicamente a marca de tempo em que um usuário pode interagir com a interface do usuário. As principais métricas para entender quanto tempo um usuário precisa esperar para usar o site sem atrasos. Boris Schapira escreveu um post detalhado sobre como medir o ITT de forma confiável.
    • Atraso da primeira entrada (FID) ou responsividade de entrada
      O tempo desde quando um usuário interage pela primeira vez com seu site até o momento em que o navegador é realmente capaz de responder a essa interação. Complementa muito bem o TTI, pois descreve a parte que falta na imagem: o que acontece quando um usuário realmente interage com o site. Destinado apenas como uma métrica RUM. Existe uma biblioteca JavaScript para medir o FID no navegador.
    • Maior pintura de conteúdo (LCP)
      Marca o ponto na linha do tempo de carregamento da página quando o conteúdo importante da página provavelmente foi carregado. A suposição é que o elemento mais importante da página seja o maior visível na janela de visualização do usuário. Se os elementos forem renderizados acima e abaixo da dobra, apenas a parte visível será considerada relevante.
    • Tempo Total de Bloqueio ( TBT )
      Uma métrica que ajuda a quantificar a gravidade de quão não interativa é uma página antes de se tornar confiavelmente interativa (ou seja, o encadeamento principal está livre de quaisquer tarefas executando mais de 50ms ( tarefas longas ) por pelo menos 5s). A métrica mede a quantidade total de tempo entre a primeira pintura e o Tempo para Interação (TTI) em que o encadeamento principal foi bloqueado por tempo suficiente para impedir a resposta de entrada. Não é de admirar, então, que um TBT baixo seja um bom indicador de bom desempenho. (obrigado, Artem, Phil)
    • Mudança de layout cumulativa ( CLS )
      A métrica destaca a frequência com que os usuários experimentam mudanças inesperadas de layout ( refluxos ) ao acessar o site. Ele examina elementos instáveis ​​e seu impacto na experiência geral. Quanto menor a pontuação, melhor.
    • Índice de velocidade
      Mede a rapidez com que o conteúdo da página é preenchido visualmente; quanto menor a pontuação, melhor. A pontuação do Índice de Velocidade é calculada com base na velocidade do progresso visual , mas é apenas um valor calculado. Também é sensível ao tamanho da janela de visualização, portanto, você precisa definir uma variedade de configurações de teste que correspondam ao seu público-alvo. Observe que está se tornando menos importante com o LCP se tornando uma métrica mais relevante ( obrigado, Boris, Artem! ).
    • Tempo de CPU gasto
      Uma métrica que mostra com que frequência e por quanto tempo o thread principal está bloqueado, trabalhando na pintura, renderização, script e carregamento. O alto tempo de CPU é um indicador claro de uma experiência ruim , ou seja, quando o usuário experimenta um atraso perceptível entre sua ação e uma resposta. Com o WebPageTest, você pode selecionar "Capture Dev Tools Timeline" na guia "Chrome" para expor o detalhamento do thread principal conforme ele é executado em qualquer dispositivo usando o WebPageTest.
    • Custos de CPU em nível de componente
      Assim como o tempo gasto na CPU , essa métrica, proposta por Stoyan Stefanov, explora o impacto do JavaScript na CPU . A ideia é usar a contagem de instruções da CPU por componente para entender seu impacto na experiência geral, isoladamente. Pode ser implementado usando Puppeteer e Chrome.
    • Índice de frustração
      Embora muitas métricas apresentadas acima expliquem quando um determinado evento acontece, o FrustrationIndex de Tim Vereecke analisa as lacunas entre as métricas em vez de analisá-las individualmente. Ele analisa os principais marcos percebidos pelo usuário final, como o título é visível, o primeiro conteúdo é visível, visualmente pronto e a página parece pronta e calcula uma pontuação indicando o nível de frustração ao carregar uma página. Quanto maior a lacuna, maior a chance de um usuário ficar frustrado. Potencialmente um bom KPI para a experiência do usuário. Tim publicou um post detalhado sobre FrustrationIndex e como ele funciona.
    • Impacto da ponderação do anúncio
      Se o seu site depende da receita gerada pela publicidade, é útil acompanhar o peso do código relacionado ao anúncio. O script de Paddy Ganti constrói duas URLs (uma normal e outra bloqueando os anúncios), solicita a geração de uma comparação de vídeo via WebPageTest e informa um delta.
    • Métricas de desvio
      Conforme observado pelos engenheiros da Wikipedia, os dados de quanta variação existe em seus resultados podem informar o quão confiáveis ​​são seus instrumentos e quanta atenção você deve prestar a desvios e outlers. Grande variação é um indicador de ajustes necessários na configuração. Também ajuda a entender se certas páginas são mais difíceis de medir de forma confiável, por exemplo, devido a scripts de terceiros que causam variação significativa. Também pode ser uma boa ideia rastrear a versão do navegador para entender os impactos no desempenho quando uma nova versão do navegador é lançada.
    • Métricas personalizadas
      As métricas personalizadas são definidas pelas necessidades do seu negócio e pela experiência do cliente. Ele exige que você identifique pixels importantes , scripts críticos , CSS necessário e ativos relevantes e meça a rapidez com que eles são entregues ao usuário. Para isso, você pode monitorar os tempos de renderização de heróis ou usar a API Performance, marcando timestamps específicos para eventos importantes para sua empresa. Além disso, você pode coletar métricas personalizadas com WebPagetest executando JavaScript arbitrário no final de um teste.

    Observe que a primeira pintura significativa (FMP) não aparece na visão geral acima. Ele costumava fornecer uma visão sobre a rapidez com que o servidor gera qualquer dado. O FMP longo geralmente indicava JavaScript bloqueando o thread principal, mas também pode estar relacionado a problemas de back-end/servidor. No entanto, a métrica foi preterida recentemente, pois parece não ser precisa em cerca de 20% dos casos. Ele foi efetivamente substituído pelo LCP, que é mais confiável e mais fácil de raciocinar. Não é mais suportado no Lighthouse. Verifique novamente as métricas e recomendações de desempenho centradas no usuário mais recentes apenas para garantir que você esteja na página segura ( obrigado, Patrick Meenan ).

    Steve Souders tem uma explicação detalhada de muitas dessas métricas. É importante observar que, embora o tempo para interação seja medido pela execução de auditorias automatizadas no chamado ambiente de laboratório , o atraso da primeira entrada representa a experiência real do usuário, com os usuários reais experimentando um atraso perceptível. Em geral, provavelmente é uma boa ideia sempre medir e acompanhar os dois.

    Dependendo do contexto do seu aplicativo, as métricas preferidas podem diferir: por exemplo, para a interface do usuário da TV Netflix, a capacidade de resposta de entrada de teclas, o uso de memória e o TTI são mais críticos e, para a Wikipedia, as primeiras/últimas alterações visuais e as métricas de tempo de CPU são mais importantes.

    Observação : tanto o FID quanto o TTI não levam em conta o comportamento de rolagem; a rolagem pode acontecer de forma independente, pois é fora do thread principal, portanto, para muitos sites de consumo de conteúdo, essas métricas podem ser muito menos importantes ( obrigado, Patrick! ).

As métricas de desempenho centradas no usuário fornecem uma visão melhor da experiência real do usuário
As métricas de desempenho centradas no usuário fornecem uma visão melhor da experiência real do usuário. First Input Delay (FID) é uma nova métrica que tenta alcançar exatamente isso. (Visualização grande)
Novos Principais Vitais da Web em uma visão geral, LCP < 2,5 s, FID < 100 ms, CLS < 0,1
Novos Principais Vitais da Web em uma visão geral, LCP < 2,5 s, FID < 100 ms, CLS < 0,1. (Core Web Vitals, via Addy Osmani)
  1. Meça e otimize os Core Web Vitals .
    Por muito tempo, as métricas de desempenho eram bastante técnicas, focando na visão de engenharia de quão rápido os servidores estão respondendo e quão rápidos os navegadores estão carregando. As métricas mudaram ao longo dos anos – tentando encontrar uma maneira de capturar a experiência real do usuário, em vez dos horários do servidor. Em maio de 2020, o Google anunciou o Core Web Vitals, um conjunto de novas métricas de desempenho focadas no usuário, cada uma representando uma faceta distinta da experiência do usuário.

    Para cada um deles, o Google recomenda uma série de metas de velocidade aceitáveis. Pelo menos 75% de todas as visualizações de página devem exceder o intervalo Bom para passar nessa avaliação. Essas métricas ganharam força rapidamente e, com o Core Web Vitals se tornando sinais de classificação para a Pesquisa do Google em maio de 2021 ( atualização do algoritmo de classificação da experiência da página ), muitas empresas voltaram sua atenção para suas pontuações de desempenho.

    Vamos detalhar cada um dos Core Web Vitals, um por um, juntamente com técnicas e ferramentas úteis para otimizar suas experiências com essas métricas em mente. (Vale a pena notar que você terminará com melhores pontuações no Core Web Vitals seguindo um conselho geral neste artigo.)

    • Maior pintura de conteúdo ( LCP ) < 2,5 seg.
      Mede o carregamento de uma página e informa o tempo de renderização da maior imagem ou bloco de texto visível na janela de visualização. Portanto, o LCP é afetado por tudo o que está adiando a renderização de informações importantes - sejam tempos de resposta lentos do servidor, CSS bloqueando, JavaScript em andamento (primário ou de terceiros), carregamento de fonte da Web, renderização cara ou operações de pintura, -carregadas de imagens, telas de esqueleto ou renderização do lado do cliente.

      Para uma boa experiência, o LCP deve ocorrer dentro de 2,5 segundos após o início do carregamento da página. Isso significa que precisamos renderizar a primeira parte visível da página o mais cedo possível. Isso exigirá CSS crítico personalizado para cada modelo, orquestrando o <head> -order e pré-buscando ativos críticos (vamos cobri-los mais tarde).

      A principal razão para uma baixa pontuação no LCP geralmente são as imagens. Para entregar um LCP em <2,5s em Fast 3G — hospedado em um servidor bem otimizado, todo estático sem renderização do lado do cliente e com uma imagem proveniente de um CDN de imagem dedicado — significa que o tamanho máximo teórico da imagem é de apenas 144 KB . É por isso que imagens responsivas são importantes, além de pré-carregar imagens críticas antecipadamente (com preload ).

      Dica rápida : para descobrir o que é considerado LCP em uma página, no DevTools você pode passar o mouse sobre o selo LCP em "Tempos" no Painel de Desempenho ( obrigado, Tim Kadlec !).

    • Atraso da primeira entrada ( FID ) < 100ms.
      Mede a capacidade de resposta da interface do usuário, ou seja, quanto tempo o navegador estava ocupado com outras tarefas antes que pudesse reagir a um evento discreto de entrada do usuário, como um toque ou um clique. Ele foi projetado para capturar atrasos resultantes do encadeamento principal estar ocupado, especialmente durante o carregamento da página.

      O objetivo é ficar entre 50 e 100 ms para cada interação. Para chegar lá, precisamos identificar tarefas longas (bloqueia o encadeamento principal por > 50ms) e dividi-las, dividir um pacote em vários pedaços, reduzir o tempo de execução do JavaScript, otimizar a busca de dados, adiar a execução de scripts de terceiros , mova o JavaScript para o thread em segundo plano com Web workers e use hidratação progressiva para reduzir os custos de reidratação em SPAs.

      Dica rápida : em geral, uma estratégia confiável para obter uma pontuação FID melhor é minimizar o trabalho no encadeamento principal , dividindo pacotes maiores em menores e atendendo o que o usuário precisa quando precisar, para que as interações do usuário não sejam atrasadas . Cobriremos mais sobre isso em detalhes abaixo.

    • Deslocamento cumulativo de layout ( CLS ) < 0,1.
      Mede a estabilidade visual da interface do usuário para garantir interações suaves e naturais, ou seja, a soma total de todas as pontuações de mudança de layout individual para cada mudança de layout inesperada que ocorre durante a vida útil da página. Uma mudança de layout individual ocorre sempre que um elemento que já estava visível muda sua posição na página. É pontuado com base no tamanho do conteúdo e na distância que ele moveu.

      Assim, toda vez que uma mudança aparece - por exemplo, quando fontes de fallback e fontes da Web têm métricas de fonte diferentes, ou anúncios, incorporações ou iframes chegando atrasados, ou dimensões de imagem/vídeo não são reservadas, ou CSS atrasado força repinturas ou alterações são injetadas por JavaScript atrasado — tem um impacto na pontuação do CLS. O valor recomendado para uma boa experiência é um CLS < 0,1.

    Vale a pena notar que os Core Web Vitals devem evoluir ao longo do tempo, com um ciclo anual previsível . Para a atualização do primeiro ano, podemos esperar que o First Contentful Paint seja promovido a Core Web Vitals, um limite FID reduzido e melhor suporte para aplicativos de página única. We might also see the responding to user inputs after load gaining more weight, along with security, privacy and accessibility (!) considerations.

    Related to Core Web Vitals, there are plenty of useful resources and articles that are worth looking into:

    • Web Vitals Leaderboard allows you to compare your scores against competition on mobile, tablet, desktop, and on 3G and 4G.
    • Core SERP Vitals, a Chrome extension that shows the Core Web Vitals from CrUX in the Google Search Results.
    • Layout Shift GIF Generator that visualizes CLS with a simple GIF (also available from the command line).
    • web-vitals library can collect and send Core Web Vitals to Google Analytics, Google Tag Manager or any other analytics endpoint.
    • Analyzing Web Vitals with WebPageTest, in which Patrick Meenan explores how WebPageTest exposes data about Core Web Vitals.
    • Optimizing with Core Web Vitals, a 50-min video with Addy Osmani, in which he highlights how to improve Core Web Vitals in an eCommerce case-study.
    • Cumulative Layout Shift in Practice and Cumulative Layout Shift in the Real World are comprehensive articles by Nic Jansma, which cover pretty much everything about CLS and how it correlates with key metrics such as Bounce Rate, Session Time or Rage Clicks.
    • What Forces Reflow, with an overview of properties or methods, when requested/called in JavaScript, that will trigger the browser to synchronously calculate the style and layout.
    • CSS Triggers shows which CSS properties trigger Layout, Paint and Composite.
    • Fixing Layout Instability is a walkthrough of using WebPageTest to identify and fix layout instability issues.
    • Cumulative Layout Shift, The Layout Instability Metric, another very detailed guide by Boris Schapira on CLS, how it's calcualted, how to measure and how to optimize for it.
    • How To Improve Core Web Vitals, a detailed guide by Simon Hearne on each of the metrics (including other Web Vitals, such as FCP, TTI, TBT), when they occur and how they are measured.

    So, are Core Web Vitals the ultimate metrics to follow ? Not quite. They are indeed exposed in most RUM solutions and platforms already, including Cloudflare, Treo, SpeedCurve, Calibre, WebPageTest (in the filmstrip view already), Newrelic, Shopify, Next.js, all Google tools (PageSpeed Insights, Lighthouse + CI, Search Console etc.) and many others.

    However, as Katie Sylor-Miller explains, some of the main problems with Core Web Vitals are the lack of cross-browser support, we don't really measure the full lifecycle of a user's experience, plus it's difficult to correlate changes in FID and CLS with business outcomes.

    As we should be expecting Core Web Vitals to evolve, it seems only reasonable to always combine Web Vitals with your custom-tailored metrics to get a better understanding of where you stand in terms of performance.

  2. Gather data on a device representative of your audience.
    To gather accurate data, we need to thoroughly choose devices to test on. In most companies, that means looking into analytics and creating user profiles based on most common device types. Yet often, analytics alone doesn't provide a complete picture. A significant portion of the target audience might be abandoning the site (and not returning back) just because their experience is too slow, and their devices are unlikely to show up as the most popular devices in analytics for that reason. So, additionally conducting research on common devices in your target group might be a good idea.

    Globally in 2020, according to the IDC, 84.8% of all shipped mobile phones are Android devices. An average consumer upgrades their phone every 2 years, and in the US phone replacement cycle is 33 months. Average bestselling phones around the world will cost under $200.

    A representative device, then, is an Android device that is at least 24 months old , costing $200 or less, running on slow 3G, 400ms RTT and 400kbps transfer, just to be slightly more pessimistic. This might be very different for your company, of course, but that's a close enough approximation of a majority of customers out there. In fact, it might be a good idea to look into current Amazon Best Sellers for your target market. ( Thanks to Tim Kadlec, Henri Helvetica and Alex Russell for the pointers! ).

    When building a new site or app, always check current Amazon Best Sellers for your target market first
    When building a new site or app, always check current Amazon Best Sellers for your target market first. (Visualização grande)

    What test devices to choose then? The ones that fit well with the profile outlined above. It's a good option to choose a slightly older Moto G4/G5 Plus, a mid-range Samsung device (Galaxy A50, S8), a good middle-of-the-road device like a Nexus 5X, Xiaomi Mi A3 or Xiaomi Redmi Note 7 and a slow device like Alcatel 1X or Cubot X19, perhaps in an open device lab. For testing on slower thermal-throttled devices, you could also get a Nexus 4, which costs just around $100.

    Also, check the chipsets used in each device and do not over-represent one chipset : a few generations of Snapdragon and Apple as well as low-end Rockchip, Mediatek would be enough (thanks, Patrick!) .

    If you don't have a device at hand, emulate mobile experience on desktop by testing on a throttled 3G network (eg 300ms RTT, 1.6 Mbps down, 0.8 Mbps up) with a throttled CPU (5× slowdown). Eventually switch over to regular 3G, slow 4G (eg 170ms RTT, 9 Mbps down, 9Mbps up), and Wi-Fi. To make the performance impact more visible, you could even introduce 2G Tuesdays or set up a throttled 3G/4G network in your office for faster testing.

    Keep in mind that on a mobile device, we should be expecting a 4×–5× slowdown compared to desktop machines. Mobile devices have different GPUs, CPU, memory and different battery characteristics. That's why it's important to have a good profile of an average device and always test on such a device.

  3. Introducing the slowest day of the week
    Introducing the slowest day of the week. Facebook has introduced 2G Tuesdays to increase visibility and sensitivity of slow connections. ( Image source)

    Luckily, there are many great options that help you automate the collection of data and measure how your website performs over time according to these metrics. Keep in mind that a good performance picture covers a set of performance metrics, lab data and field data:

    • Synthetic testing tools collect lab data in a reproducible environment with predefined device and network settings (eg Lighthouse , Calibre , WebPageTest ) and
    • Real User Monitoring ( RUM ) tools evaluate user interactions continuously and collect field data (eg SpeedCurve , New Relic — the tools provide synthetic testing, too).

    The former is particularly useful during development as it will help you identify, isolate and fix performance issues while working on the product. The latter is useful for long-term maintenance as it will help you understand your performance bottlenecks as they are happening live — when users actually access the site.

    By tapping into built-in RUM APIs such as Navigation Timing, Resource Timing, Paint Timing, Long Tasks, etc., synthetic testing tools and RUM together provide a complete picture of performance in your application. You could use Calibre, Treo, SpeedCurve, mPulse and Boomerang, Sitespeed.io, which all are great options for performance monitoring. Furthermore, with Server Timing header, you could even monitor back-end and front-end performance all in one place.

    Note : It's always a safer bet to choose network-level throttlers, external to the browser, as, for example, DevTools has issues interacting with HTTP/2 push, due to the way it's implemented ( thanks, Yoav, Patrick !). For Mac OS, we can use Network Link Conditioner, for Windows Windows Traffic Shaper, for Linux netem, and for FreeBSD dummynet.

    As it's likely that you'll be testing in Lighthouse, keep in mind that you can:

    • use Lighthouse CI to track Lighthouse scores over time (it's quite impressive),
    • run Lighthouse in GitHub Actions to get a Lighthouse report alongside every PR,
    • run a Lighthouse performance audit on every page of a site (via Lightouse Parade), with an output saved as CSV,
    • use Lighthouse Scores Calculator and Lighthouse metric weights if you need to dive into more detail.
    • Lighthouse is available for Firefox as well, but under the hood it uses the PageSpeed Insights API and generates a report based on a headless Chrome 79 User-Agent.
Lighthouse CI is quite remarkable: a suite of tools to continuously run, save, retrieve, and assert against Lighthouse results
Lighthouse CI is quite remarkable: a suite of tools to continuously run, save, retrieve, and assert against Lighthouse results. (Visualização grande)
  1. Set up "clean" and "customer" profiles for testing.
    While running tests in passive monitoring tools, it's a common strategy to turn off anti-virus and background CPU tasks, remove background bandwidth transfers and test with a clean user profile without browser extensions to avoid skewed results (in Firefox, and in Chrome).
    DebugBear's report highlights 20 slowest extensions, including password managers, ad-blockers and popular applications like Evernote and Grammarly
    DebugBear's report highlights 20 slowest extensions, including password managers, ad-blockers and popular applications like Evernote and Grammarly. (Visualização grande)

    However, it's also a good idea to study which browser extensions your customers use frequently, and test with dedicated "customer" profiles as well. In fact, some extensions might have a profound performance impact (2020 Chrome Extension Performance Report) on your application, and if your users use them a lot, you might want to account for it up front. Hence, "clean" profile results alone are overly optimistic and can be crushed in real-life scenarios.

  2. Compartilhe as metas de desempenho com seus colegas.
    Certifique-se de que as metas de desempenho sejam familiares a todos os membros de sua equipe para evitar mal-entendidos no futuro. Cada decisão – seja design, marketing ou qualquer coisa intermediária – tem implicações de desempenho , e a distribuição de responsabilidade e propriedade por toda a equipe simplificaria as decisões focadas no desempenho posteriormente. Mapeie as decisões de design em relação ao orçamento de desempenho e às prioridades definidas desde o início.

Definir metas realistas

  1. Tempo de resposta de 100 milissegundos, 60 fps.
    Para que uma interação seja suave, a interface tem 100 ms para responder à entrada do usuário. Mais do que isso, e o usuário percebe o aplicativo como lento. O RAIL, um modelo de desempenho centrado no usuário, oferece metas saudáveis : para permitir uma resposta de <100 milissegundos, a página deve devolver o controle ao encadeamento principal no máximo a cada <50 milissegundos. A latência de entrada estimada nos diz se estamos atingindo esse limite e, idealmente, deve estar abaixo de 50ms. Para pontos de alta pressão como animação, é melhor não fazer mais nada onde puder e o mínimo absoluto onde não puder.

    RAIL, um modelo de desempenho centrado no usuário.
    RAIL, um modelo de desempenho centrado no usuário.

    Além disso, cada quadro de animação deve ser concluído em menos de 16 milissegundos, atingindo assim 60 quadros por segundo (1 segundo ÷ 60 = 16,6 milissegundos) — preferencialmente menos de 10 milissegundos. Como o navegador precisa de tempo para pintar o novo quadro na tela, seu código deve terminar a execução antes de atingir a marca de 16,6 milissegundos. Estamos começando a ter conversas sobre 120fps (por exemplo, as telas do iPad Pro rodam a 120Hz) e a Surma cobriu algumas soluções de desempenho de renderização para 120fps, mas provavelmente não é um alvo que estamos olhando ainda .

    Seja pessimista nas expectativas de desempenho, mas seja otimista no design da interface e use o tempo ocioso com sabedoria (verifique idlize, idle-until-urgente e react-idle). Obviamente, esses destinos se aplicam ao desempenho de tempo de execução, em vez de desempenho de carregamento.

  2. FID < 100 ms, LCP < 2,5 s, TTI < 5 s em 3G, orçamento de tamanho de arquivo crítico < 170 KB (gzipado).
    Embora possa ser muito difícil de alcançar, um bom objetivo final seria o Tempo para Interação abaixo de 5s e, para visitas repetidas, a meta de menos de 2s (alcançável apenas com um service worker). Apontar para a maior pintura de conteúdo de menos de 2,5s e minimizar o tempo total de bloqueio e a mudança de layout cumulativa . Um atraso de primeira entrada aceitável é inferior a 100ms–70ms. Como mencionado acima, estamos considerando que a linha de base seja um telefone Android de US$ 200 (por exemplo, Moto G4) em uma rede 3G lenta, emulado a 400ms RTT e velocidade de transferência de 400kbps.

    Temos duas restrições principais que moldam efetivamente uma meta razoável para a entrega rápida do conteúdo na web. Por um lado, temos restrições de entrega de rede devido ao TCP Slow Start. Os primeiros 14 KB do HTML — 10 pacotes TCP, cada um com 1.460 bytes, perfazendo cerca de 14,25 KB, embora não devam ser tomados literalmente — é o trecho de carga útil mais crítico e a única parte do orçamento que pode ser entregue na primeira viagem de ida e volta ( que é tudo o que você obtém em 1 segundo a 400ms RTT devido aos horários de ativação do celular).

    Rede de navegadores de alto desempenho por Ilya Grigorik
    Com conexões TCP, começamos com uma pequena janela de congestionamento e a dobramos para cada viagem de ida e volta. Na primeira viagem de ida e volta, cabem 14 KB. De: Rede de navegadores de alto desempenho por Ilya Grigorik. (Visualização grande)

    ( Observação : como o TCP geralmente subutiliza a conexão de rede em uma quantidade significativa, o Google desenvolveu a largura de banda do gargalo TCP e o RRT ( BBR ), um algoritmo de controle de fluxo TCP controlado por atraso do TCP. Projetado para a Web moderna, ele responde ao congestionamento real, em vez de perda de pacotes como o TCP faz, é significativamente mais rápido, com maior taxa de transferência e menor latência - e o algoritmo funciona de maneira diferente. ( obrigado, Victor, Barry! )

    Por outro lado, temos restrições de hardware na memória e CPU devido aos tempos de análise e execução de JavaScript (falaremos sobre eles em detalhes mais tarde). Para atingir os objetivos declarados no primeiro parágrafo, devemos considerar o orçamento de tamanho de arquivo crítico para JavaScript. As opiniões variam sobre qual deve ser esse orçamento (e depende muito da natureza do seu projeto), mas um orçamento de 170 KB de JavaScript já compactado levaria até 1s para analisar e compilar em um telefone de médio porte. Supondo que 170 KB se expanda para 3 vezes esse tamanho quando descompactado (0,7 MB), isso já pode ser a sentença de morte de uma experiência de usuário "decente" em um Moto G4/G5 Plus.

    No caso do site da Wikipedia, em 2020, globalmente, a execução de código ficou 19% mais rápida para os usuários da Wikipedia. Portanto, se suas métricas de desempenho da Web ano a ano permanecerem estáveis, isso geralmente é um sinal de alerta, pois você está realmente regredindo à medida que o ambiente continua melhorando (detalhes em uma postagem no blog de Gilles Dubuc).

    Se você deseja atingir mercados em crescimento, como o Sudeste Asiático, África ou Índia, terá que analisar um conjunto muito diferente de restrições. Addy Osmani cobre as principais restrições de recursos de telefonia, como poucos dispositivos de baixo custo e alta qualidade, indisponibilidade de redes de alta qualidade e dados móveis caros - juntamente com orçamento PRPL-30 e diretrizes de desenvolvimento para esses ambientes.

    De acordo com Addy Osmani, um tamanho recomendado para rotas de carregamento lento também é inferior a 35 KB
    De acordo com Addy Osmani, um tamanho recomendado para rotas de carregamento lento também é inferior a 35 KB. (Visualização grande)
    Addy Osmani sugere um orçamento de desempenho PRPL-30 (30 KB compactado + pacote inicial reduzido) se tiver como alvo um telefone comum
    Addy Osmani sugere um orçamento de desempenho PRPL-30 (30 KB compactado + pacote inicial minificado) se tiver como alvo um telefone comum. (Visualização grande)

    Na verdade, Alex Russell, do Google, recomenda apontar para 130–170 KB compactados como um limite superior razoável. Em cenários do mundo real, a maioria dos produtos não chega nem perto: um tamanho médio de pacote hoje é de cerca de 452 KB, o que representa um aumento de 53,6% em relação ao início de 2015. Em um dispositivo móvel de classe média, isso representa 12 a 20 segundos para o Tempo -Para-Interativo .

    Benchmarks de desempenho de CPU Geekbench para os smartphones mais vendidos do mundo em 2019. JavaScript enfatiza o desempenho de núcleo único e é vinculado à CPU
    Referências de desempenho de CPU do Geekbench para os smartphones mais vendidos globalmente em 2019. O JavaScript enfatiza o desempenho de núcleo único (lembre-se, é inerentemente mais single-thread do que o restante da plataforma da Web) e é vinculado à CPU. Do artigo de Addy "Carregando páginas da Web rapidamente em um telefone de US $ 20". (Visualização grande)

    Também poderíamos ir além do orçamento do tamanho do pacote. Por exemplo, podemos definir orçamentos de desempenho com base nas atividades do thread principal do navegador, ou seja, tempo de pintura antes de iniciar a renderização ou rastrear porcos de CPU front-end. Ferramentas como Calibre, SpeedCurve e Bundlesize podem ajudá-lo a manter seus orçamentos sob controle e podem ser integrados ao seu processo de construção.

    Por fim, um orçamento de desempenho provavelmente não deve ser um valor fixo . Dependendo da conexão de rede, os orçamentos de desempenho devem se adaptar, mas a carga útil em uma conexão mais lenta é muito mais "caro", independentemente de como eles são usados.

    Nota : Pode parecer estranho definir orçamentos tão rígidos em tempos de HTTP/2 generalizado, próximos 5G e HTTP/3, telefones celulares em rápida evolução e SPAs florescentes. No entanto, eles parecem razoáveis ​​quando lidamos com a natureza imprevisível da rede e do hardware, incluindo tudo, desde redes congestionadas até infraestrutura em desenvolvimento lento, limites de dados, navegadores proxy, modo de salvamento de dados e tarifas de roaming sorrateiras.

De 'Fast By Default: Modern Loading Best Practices' por Addy Osmani
De Fast By Default: Práticas recomendadas de carregamento moderno por Addy Osmani (Slide 19)
Os orçamentos de desempenho devem se adaptar de acordo com as condições da rede para um dispositivo móvel médio
Os orçamentos de desempenho devem se adaptar de acordo com as condições da rede para um dispositivo móvel médio. (Fonte da imagem: Katie Hempenius) (Visualização grande)

Definindo o Ambiente

  1. Escolha e configure suas ferramentas de construção.
    Não dê muita atenção ao que é supostamente legal hoje em dia. Atenha-se ao seu ambiente de construção, seja Grunt, Gulp, Webpack, Parcel ou uma combinação de ferramentas. Contanto que você esteja obtendo os resultados de que precisa e não tenha problemas para manter seu processo de compilação, você está indo muito bem.

    Entre as ferramentas de compilação, o Rollup continua ganhando força, assim como o Snowpack, mas o Webpack parece ser o mais estabelecido, com literalmente centenas de plugins disponíveis para otimizar o tamanho de suas compilações. Fique atento ao Roteiro do Webpack 2021.

    Uma das estratégias mais notáveis ​​​​que apareceram recentemente é o agrupamento granular com Webpack no Next.js e no Gatsby para minimizar o código duplicado. Por padrão, os módulos que não são compartilhados em todos os pontos de entrada podem ser solicitados para rotas que não o utilizam. Isso acaba se tornando uma sobrecarga à medida que mais código é baixado do que o necessário. Com a fragmentação granular no Next.js, podemos usar um arquivo de manifesto de compilação do lado do servidor para determinar quais fragmentos de saída são usados ​​por diferentes pontos de entrada.

    Para reduzir o código duplicado em projetos Webpack, podemos usar fragmentação granular, habilitada em Next.js e Gatsby por padrão
    Para reduzir o código duplicado em projetos Webpack, podemos usar fragmentação granular, habilitada em Next.js e Gatsby por padrão. Crédito da imagem: Addy Osmani. (Visualização grande)

    Com SplitChunksPlugin, vários blocos divididos são criados dependendo de várias condições para evitar a busca de código duplicado em várias rotas. Isso melhora o tempo de carregamento da página e o armazenamento em cache durante as navegações. Enviado no Next.js 9.2 e no Gatsby v2.20.7.

    No entanto, começar com o Webpack pode ser difícil. Então, se você quiser mergulhar no Webpack, existem alguns ótimos recursos por aí:

    • A documentação do Webpack — obviamente — é um bom ponto de partida, assim como o Webpack — The Confusing Bits de Raja Rao e An Annotated Webpack Config de Andrew Welch.
    • Sean Larkin tem um curso gratuito sobre Webpack: The Core Concepts e Jeffrey Way lançou um curso gratuito fantástico sobre Webpack para todos. Ambos são ótimas introduções para mergulhar no Webpack.
    • Webpack Fundamentals é um curso de 4h muito abrangente com Sean Larkin, lançado pela FrontendMasters.
    • Exemplos de Webpack tem centenas de configurações de Webpack prontas para uso, categorizadas por tópico e finalidade. Bônus: há também um configurador de configuração do Webpack que gera um arquivo de configuração básica.
    • awesome-webpack é uma lista com curadoria de recursos, bibliotecas e ferramentas úteis do Webpack, incluindo artigos, vídeos, cursos, livros e exemplos para projetos Angular, React e framework-agnósticos.
    • A jornada para a rápida criação de ativos de produção com o Webpack é o estudo de caso da Etsy sobre como a equipe passou de um sistema de compilação JavaScript baseado em RequireJS para o uso do Webpack e como eles otimizaram suas compilações, gerenciando mais de 13.200 ativos em 4 minutos em média.
    • Dicas de desempenho do Webpack é um tópico de mina de ouro de Ivan Akulov, apresentando muitas dicas focadas em desempenho, incluindo aquelas focadas especificamente no Webpack.
    • awesome-webpack-perf é um repositório GitHub de mina de ouro com ferramentas e plugins úteis do Webpack para desempenho. Também mantido por Ivan Akulov.
Uma visualização da jornada do Etsy para construções rápidas de produção com o Webpack
A jornada do Etsy para uma produção rápida com Webpack (via Addy Osmani) (visualização grande)
  1. Use o aprimoramento progressivo como padrão.
    Ainda assim, depois de todos esses anos, manter o aprimoramento progressivo como princípio orientador de sua arquitetura e implantação de front-end é uma aposta segura. Projete e construa a experiência principal primeiro e, em seguida, aprimore a experiência com recursos avançados para navegadores capazes, criando experiências resilientes. Se o seu site for executado rápido em uma máquina lenta com uma tela ruim em um navegador ruim em uma rede abaixo do ideal, ele só será executado mais rápido em uma máquina rápida com um bom navegador em uma rede decente.

    Na verdade, com o serviço de módulo adaptável, parece que estamos levando o aprimoramento progressivo a outro nível, oferecendo experiências básicas "leves" para dispositivos de baixo custo e aprimorando com recursos mais sofisticados para dispositivos de ponta. O aprimoramento progressivo provavelmente não desaparecerá tão cedo.

  2. Escolha uma linha de base de desempenho forte.
    Com tantas incógnitas afetando o carregamento - a rede, limitação térmica, remoção de cache, scripts de terceiros, padrões de bloqueio de analisador, E/S de disco, latência de IPC, extensões instaladas, software antivírus e firewalls, tarefas de CPU em segundo plano, restrições de hardware e memória, diferenças no cache L2/L3, RTTS — JavaScript tem o custo mais alto da experiência, ao lado de fontes da Web que bloqueiam a renderização por padrão e imagens geralmente consumindo muita memória. Com os gargalos de desempenho se afastando do servidor para o cliente, como desenvolvedores, temos que considerar todas essas incógnitas com muito mais detalhes.

    Com um orçamento de 170 KB que já contém o caminho crítico HTML/CSS/JavaScript, roteador, gerenciamento de estado, utilitários, estrutura e a lógica do aplicativo, temos que examinar minuciosamente o custo de transferência de rede, o tempo de análise/compilação e o custo de tempo de execução do quadro de nossa escolha. Felizmente, vimos uma grande melhoria nos últimos anos na rapidez com que os navegadores podem analisar e compilar scripts. No entanto, a execução do JavaScript ainda é o principal gargalo, portanto, prestar muita atenção ao tempo de execução do script e à rede pode ser impactante.

    Tim Kadlec conduziu uma pesquisa fantástica sobre o desempenho de frameworks modernos e os resumiu no artigo "Os frameworks JavaScript têm um custo". Costumamos falar sobre o impacto de estruturas independentes, mas, como observa Tim, na prática, não é incomum ter várias estruturas em uso . Talvez uma versão mais antiga do jQuery que está sendo migrada lentamente para uma estrutura moderna, juntamente com alguns aplicativos legados usando uma versão mais antiga do Angular. Portanto, é mais razoável explorar o custo cumulativo de bytes JavaScript e o tempo de execução da CPU que podem facilmente tornar as experiências do usuário quase inúteis, mesmo em dispositivos de última geração.

    Em geral, as estruturas modernas não priorizam dispositivos menos poderosos , portanto, as experiências em um telefone e em um desktop geralmente serão drasticamente diferentes em termos de desempenho. De acordo com a pesquisa, sites com React ou Angular gastam mais tempo na CPU do que outros (o que obviamente não quer dizer que React é mais caro na CPU do que Vue.js).

    De acordo com Tim, uma coisa é óbvia: "se você estiver usando uma estrutura para construir seu site, estará fazendo uma troca em termos de desempenho inicial - mesmo nos melhores cenários".

O custo dos frameworks, tempo de CPU JavaScript: os sites SPA têm um desempenho ruim
O custo dos frameworks, adeus JavaScript: os sites SPA (ainda) têm um desempenho ruim
Tempo de CPU relacionado a scripts para dispositivos móveis e bytes JavaScript para dispositivos desktopv. Em geral, sites com React ou Angular gastam mais tempo na CPU do que outros. Mas isso depende de como você constrói o site. Pesquisa de Tim Kadlec. (Visualização grande)
  1. Avalie estruturas e dependências.
    Agora, nem todo projeto precisa de uma estrutura e nem toda página de um aplicativo de página única precisa carregar uma estrutura. No caso da Netflix, "remover o React, várias bibliotecas e o código de aplicativo correspondente do lado do cliente reduziu a quantidade total de JavaScript em mais de 200 KB, causando uma redução de mais de 50% no tempo de interatividade da Netflix para a página inicial desconectada ." A equipe então utilizou o tempo gasto pelos usuários na página de destino para pré-buscar o React para as páginas subsequentes que os usuários provavelmente acessariam (leia para obter detalhes).

    E daí se você remover completamente uma estrutura existente em páginas críticas? Com o Gatsby, você pode verificar o gatsby-plugin-no-javascript que remove todos os arquivos JavaScript criados pelo Gatsby dos arquivos HTML estáticos. No Vercel, você também pode permitir desabilitar o JavaScript em tempo de execução em produção para determinadas páginas (experimental).

    Depois que uma estrutura for escolhida, permaneceremos com ela por pelo menos alguns anos, portanto, se precisarmos usar uma, precisamos garantir que nossa escolha seja informada e bem considerada - e isso vale especialmente para as principais métricas de desempenho que se importar.

    Os dados mostram que, por padrão, os frameworks são bastante caros: 58,6% das páginas React enviam mais de 1 MB de JavaScript e 36% dos carregamentos de página Vue.js têm uma primeira pintura de conteúdo de <1,5s. De acordo com um estudo da Ankur Sethi, "seu aplicativo React nunca carregará mais rápido do que cerca de 1,1 segundo em um telefone médio na Índia, não importa o quanto você o otimize. Seu aplicativo Angular sempre levará pelo menos 2,7 segundos para inicializar. O os usuários do seu aplicativo Vue precisarão esperar pelo menos 1 segundo antes de poderem começar a usá-lo." Você pode não estar segmentando a Índia como seu mercado principal, mas os usuários que acessam seu site com condições de rede abaixo do ideal terão uma experiência comparável.

    É claro que é possível fazer SPAs rápidos, mas eles não são rápidos de fábrica, então precisamos levar em conta o tempo e o esforço necessários para fazê -los e mantê -los rápidos. Provavelmente será mais fácil escolher um custo de desempenho de linha de base leve desde o início.

    Então, como escolhemos uma estrutura ? É uma boa ideia considerar pelo menos o custo total em tamanho + tempos de execução iniciais antes de escolher uma opção; opções leves como Preact, Inferno, Vue, Svelte, Alpine ou Polymer podem fazer o trabalho muito bem. O tamanho de sua linha de base definirá as restrições para o código do seu aplicativo.

    Conforme observado por Seb Markbage, uma boa maneira de medir os custos iniciais de estruturas é primeiro renderizar uma visualização, excluí-la e renderizar novamente , pois isso pode informar como a estrutura é dimensionada. A primeira renderização tende a aquecer um monte de código compilado preguiçosamente, do qual uma árvore maior pode se beneficiar quando for dimensionada. A segunda renderização é basicamente uma emulação de como a reutilização de código em uma página afeta as características de desempenho à medida que a página cresce em complexidade.

    Você pode avaliar seus candidatos (ou qualquer biblioteca JavaScript em geral) no sistema de pontuação de escala de 12 pontos da Sacha Greif explorando recursos, acessibilidade, estabilidade, desempenho, ecossistema de pacotes , comunidade, curva de aprendizado, documentação, ferramentas, histórico , equipe, compatibilidade, segurança por exemplo.

    Perf Track rastreia o desempenho da estrutura em escala
    Perf Track rastreia o desempenho da estrutura em escala. (Visualização grande)

    Você também pode confiar nos dados coletados na web por um longo período de tempo. Por exemplo, o Perf Track rastreia o desempenho da estrutura em escala, mostrando pontuações de Core Web Vitals agregadas de origem para sites criados em Angular, React, Vue, Polymer, Preact, Ember, Svelte e AMP. Você pode até especificar e comparar sites criados com Gatsby, Next.js ou Create React App, bem como sites criados com Nuxt.js (Vue) ou Sapper (Svelte).

    Um bom ponto de partida é escolher uma boa pilha padrão para seu aplicativo. Gatsby (React), Next.js (React), Vuepress (Vue), Preact CLI e PWA Starter Kit fornecem padrões razoáveis ​​para carregamento rápido pronto para uso em hardware móvel médio. Além disso, dê uma olhada na orientação de desempenho específica do framework web.dev para React e Angular ( obrigado, Phillip! ).

    E talvez você possa adotar uma abordagem um pouco mais atualizada para criar aplicativos de página única – Turbolinks, uma biblioteca JavaScript de 15 KB que usa HTML em vez de JSON para renderizar visualizações. Portanto, quando você segue um link, o Turbolinks busca automaticamente a página, troca seu <body> e mescla seu <head> , tudo sem incorrer no custo de um carregamento completo da página. Você pode verificar detalhes rápidos e documentação completa sobre a pilha (Hotwire).

Um gráfico semelhante a um histograma mostrando o desempenho de computação dos telefones mais vendidos
Desempenho de CPU e computação dos telefones mais vendidos (Crédito da imagem: Addy Osmani) (visualização grande)
  1. Renderização do lado do cliente ou renderização do lado do servidor? Ambos!
    Essa é uma conversa bastante acalorada para se ter. A abordagem final seria configurar algum tipo de inicialização progressiva: use a renderização do lado do servidor para obter uma primeira exibição de conteúdo rápida, mas também inclua algum JavaScript mínimo necessário para manter o tempo de interação próximo ao da primeira exibição de conteúdo. Se o JavaScript chegar muito tarde após o FCP, o navegador bloqueará o thread principal ao analisar, compilar e executar o JavaScript descoberto tardiamente, prejudicando a interatividade do site ou aplicativo.

    Para evitá-lo, sempre divida a execução de funções em tarefas assíncronas separadas e, sempre que possível, use requestIdleCallback . Considere o carregamento lento de partes da interface do usuário usando o suporte dynamic import() do WebPack, evitando o custo de carregamento, análise e compilação até que os usuários realmente precisem deles ( obrigado Addy! ).

    Como mencionado acima, o Time to Interactive (TTI) nos informa o tempo entre a navegação e a interatividade. Em detalhes, a métrica é definida observando a primeira janela de cinco segundos após a renderização do conteúdo inicial, na qual nenhuma tarefa JavaScript leva mais de 50 ms ( Long Tasks ). Se ocorrer uma tarefa com mais de 50ms, a busca por uma janela de cinco segundos recomeça. Como resultado, o navegador primeiro assumirá que atingiu Interactive , apenas para mudar para Frozen , apenas para eventualmente voltar para Interactive .

    Assim que chegarmos ao Interactive , podemos - sob demanda ou conforme o tempo permitir - inicializar partes não essenciais do aplicativo. Infelizmente, como Paul Lewis notou, os frameworks normalmente não têm um conceito simples de prioridade que possa ser apresentado aos desenvolvedores e, portanto, a inicialização progressiva não é fácil de implementar com a maioria das bibliotecas e frameworks.

    Ainda assim, estamos chegando lá. Atualmente, existem algumas opções que podemos explorar, e Houssein Djirdeh e Jason Miller fornecem uma excelente visão geral dessas opções em sua palestra sobre Rendering on the Web e no artigo de Jason e Addy sobre Modern Front-End Architectures. A visão geral abaixo é baseada em seu trabalho estelar.

    • Renderização completa do lado do servidor (SSR)
      No SSR clássico, como o WordPress, todas as solicitações são tratadas inteiramente no servidor. O conteúdo solicitado é retornado como uma página HTML finalizada e os navegadores podem renderizá-lo imediatamente. Portanto, os aplicativos SSR não podem realmente usar as APIs DOM, por exemplo. A diferença entre a primeira pintura de conteúdo e o tempo de interação geralmente é pequena, e a página pode ser renderizada imediatamente à medida que o HTML está sendo transmitido para o navegador.

      Isso evita viagens de ida e volta adicionais para busca e modelagem de dados no cliente, pois isso é tratado antes que o navegador obtenha uma resposta. No entanto, acabamos com um tempo de raciocínio do servidor mais longo e, consequentemente, com o tempo até o primeiro byte e não usamos recursos responsivos e ricos de aplicativos modernos.

    • Renderização estática
      Construímos o produto como um aplicativo de página única, mas todas as páginas são pré-renderizadas para HTML estático com JavaScript mínimo como uma etapa de construção. Isso significa que, com renderização estática, produzimos arquivos HTML individuais para cada URL possível com antecedência, algo que poucos aplicativos podem pagar. Mas como o HTML de uma página não precisa ser gerado instantaneamente, podemos obter um tempo até o primeiro byte consistentemente rápido. Assim, podemos exibir uma página de destino rapidamente e, em seguida, pré-buscar uma estrutura SPA para as páginas subsequentes. A Netflix adotou essa abordagem, reduzindo o carregamento e o tempo para interação em 50%.

    • Renderização do lado do servidor com (Re)hidratação (Renderização universal, SSR + CSR)
      Podemos tentar usar o melhor dos dois mundos – as abordagens SSR e CSR. Com hidratação na mistura, a página HTML retornada do servidor também contém um script que carrega um aplicativo completo do lado do cliente. Idealmente, que obtenha uma primeira pintura de conteúdo rápida (como SSR) e continue a renderização com (re)hidratação. Infelizmente, isso raramente acontece. Mais frequentemente, a página parece pronta, mas não pode responder à entrada do usuário, produzindo cliques de raiva e abandonos.

      Com o React, podemos usar o módulo ReactDOMServer em um servidor Node como o Express, e então chamar o método renderToString para renderizar os componentes de nível superior como uma string HTML estática.

      Com o Vue.js, podemos usar o vue-server-renderer para renderizar uma instância do Vue em HTML usando renderToString . Em Angular, podemos usar @nguniversal para transformar solicitações de clientes em páginas HTML totalmente renderizadas pelo servidor. Uma experiência totalmente renderizada pelo servidor também pode ser obtida imediatamente com Next.js (React) ou Nuxt.js (Vue).

      A abordagem tem suas desvantagens. Como resultado, ganhamos total flexibilidade de aplicativos do lado do cliente ao mesmo tempo em que fornecemos renderização mais rápida do lado do servidor, mas também acabamos com uma lacuna maior entre a primeira pintura de conteúdo e o tempo de interação e um atraso maior na primeira entrada. A reidratação é muito cara e, geralmente, essa estratégia sozinha não será boa o suficiente, pois atrasa muito o Time To Interactive.

    • Streaming de renderização do lado do servidor com hidratação progressiva (SSR + CSR)
      Para minimizar a lacuna entre o tempo de interação e a primeira pintura de conteúdo, renderizamos várias solicitações de uma só vez e enviamos o conteúdo em partes à medida que são gerados. Portanto, não precisamos esperar pela sequência completa de HTML antes de enviar o conteúdo para o navegador e, portanto, melhorar o tempo até o primeiro byte.

      Em React, em vez de renderToString() , podemos usar renderToNodeStream() para canalizar a resposta e enviar o HTML em partes. No Vue, podemos usar renderToStream() que pode ser canalizado e transmitido. Com o React Suspense, podemos usar renderização assíncrona para esse propósito também.

      No lado do cliente, em vez de inicializar todo o aplicativo de uma vez, inicializamos os componentes progressivamente . As seções dos aplicativos são primeiro divididas em scripts autônomos com divisão de código e, em seguida, hidratadas gradualmente (na ordem de nossas prioridades). Na verdade, podemos hidratar os componentes críticos primeiro, enquanto o resto pode ser hidratado mais tarde. A função de renderização do lado do cliente e do lado do servidor pode ser definida de forma diferente por componente. Também podemos adiar a hidratação de alguns componentes até que eles apareçam ou sejam necessários para a interação do usuário ou quando o navegador estiver ocioso.

      Para o Vue, Markus Oberlehner publicou um guia sobre como reduzir o tempo de interação de aplicativos SSR usando hidratação na interação do usuário, bem como vue-lazy-hydration, um plug-in em estágio inicial que permite a hidratação do componente na visibilidade ou interação específica do usuário. A equipe Angular trabalha na hidratação progressiva com Ivy Universal. Você também pode implementar a hidratação parcial com Preact e Next.js.

    • Renderização Trisomórfica
      Com os service workers em vigor, podemos usar a renderização do servidor de streaming para navegações iniciais/não JS e, em seguida, fazer com que o service worker assuma a renderização do HTML para navegações após a instalação. Nesse caso, o service worker pré-renderiza o conteúdo e habilita as navegações no estilo SPA para renderizar novas exibições na mesma sessão. Funciona bem quando você pode compartilhar o mesmo código de modelagem e roteamento entre o servidor, a página do cliente e o service worker.

    Uma ilustração mostrando como a renderização trisomórfica funciona em 3 lugares, como renderização DOM, pré-renderização de service worker e renderização do lado do servidor
    Renderização trisomórfica, com a mesma renderização de código em 3 lugares: no servidor, no DOM ou em um service worker. (Fonte da imagem: Google Developers) (Visualização grande)
    • CSR com pré-renderização
      A pré-renderização é semelhante à renderização do lado do servidor, mas em vez de renderizar páginas no servidor dinamicamente, renderizamos o aplicativo para HTML estático no momento da compilação. Embora as páginas estáticas sejam totalmente interativas sem muito JavaScript do lado do cliente, a pré- renderização funciona de maneira diferente . Basicamente, ele captura o estado inicial de um aplicativo do lado do cliente como HTML estático em tempo de compilação, enquanto com a pré-renderização o aplicativo deve ser inicializado no cliente para que as páginas sejam interativas.

      Com o Next.js, podemos usar a exportação de HTML estático pré-renderizando um aplicativo para HTML estático. No Gatsby, um gerador de site estático de código aberto que usa React, usa o método renderToStaticMarkup em vez do método renderToString durante as compilações, com o bloco JS principal sendo pré-carregado e as rotas futuras são pré-buscadas, sem atributos DOM que não são necessários para páginas estáticas simples.

      Para o Vue, podemos usar o Vuepress para atingir o mesmo objetivo. Você também pode usar o prerender-loader com o Webpack. O Navi também fornece renderização estática.

      O resultado é um melhor tempo até o primeiro byte e primeira pintura de conteúdo, e reduzimos a lacuna entre o tempo de interação e a primeira pintura de conteúdo. Não podemos usar a abordagem se espera-se que o conteúdo mude muito. Além disso, todos os URLs precisam ser conhecidos com antecedência para gerar todas as páginas. Portanto, alguns componentes podem ser renderizados usando pré-renderização, mas se precisarmos de algo dinâmico, teremos que confiar no aplicativo para buscar o conteúdo.

    • Renderização completa do lado do cliente (CSR)
      Toda a lógica, renderização e inicialização são feitas no cliente. O resultado geralmente é uma enorme lacuna entre o tempo para interação e a primeira pintura de conteúdo. Como resultado, os aplicativos geralmente parecem lentos , pois o aplicativo inteiro precisa ser inicializado no cliente para renderizar qualquer coisa.

      Como o JavaScript tem um custo de desempenho, à medida que a quantidade de JavaScript cresce com um aplicativo, a divisão agressiva de código e o adiamento do JavaScript serão absolutamente necessários para domar o impacto do JavaScript. Para esses casos, uma renderização do lado do servidor geralmente será uma abordagem melhor, caso não seja necessária muita interatividade. Se não for uma opção, considere usar o App Shell Model.

      Em geral, o SSR é mais rápido que o CSR. Ainda assim, é uma implementação bastante frequente para muitos aplicativos por aí.

    Então, do lado do cliente ou do lado do servidor? Em geral, é uma boa ideia limitar o uso de estruturas totalmente do lado do cliente a páginas que as exigem absolutamente. Para aplicativos avançados, também não é uma boa ideia confiar apenas na renderização do lado do servidor. Tanto a renderização do servidor quanto a renderização do cliente são um desastre se forem mal feitas.

    Se você está inclinado para CSR ou SSR, certifique-se de renderizar pixels importantes o mais rápido possível e minimizar a lacuna entre essa renderização e o tempo de interação. Considere a pré-renderização se suas páginas não mudarem muito e adie a inicialização dos frameworks se puder. Transmita HTML em partes com renderização do lado do servidor e implemente hidratação progressiva para renderização do lado do cliente — e hidrate-se na visibilidade, interação ou durante o tempo ocioso para obter o melhor dos dois mundos.

Uma tabela comparando opções para renderização do lado do cliente versus renderização do lado do servidor
O espectro de opções para renderização no lado do cliente versus no lado do servidor. Além disso, confira a palestra de Jason e Houssein no Google I/O sobre Performance Implications of Application Architecture. (Fonte da imagem: Jason Miller) (Visualização grande)
Um exemplo do site do AirBnB mostrando sem hidratação progressiva à esquerda e com hidratação progressiva à direita
O AirBnB vem experimentando hidratação progressiva; eles adiam componentes desnecessários, carregam na interação do usuário (scroll) ou durante o tempo ocioso e os testes mostram que isso pode melhorar o TTI. (Visualização grande)
  1. Quanto podemos servir estaticamente?
    Esteja você trabalhando em um aplicativo grande ou em um site pequeno, vale a pena considerar qual conteúdo pode ser servido estaticamente a partir de um CDN (ou seja, JAM Stack), em vez de ser gerado dinamicamente em tempo real. Mesmo se você tiver milhares de produtos e centenas de filtros com muitas opções de personalização, ainda poderá servir suas páginas de destino críticas estaticamente e desacoplar essas páginas da estrutura de sua escolha.

    Existem muitos geradores de sites estáticos e as páginas que eles geram geralmente são muito rápidas. The more content we can pre-build ahead of time instead of generating page views on a server or client at request time, the better performance we will achieve.

    In Building Partially Hydrated, Progressively Enhanced Static Websites, Markus Oberlehner shows how to build out websites with a static site generator and an SPA, while achieving progressive enhancement and a minimal JavaScript bundle size. Markus uses Eleventy and Preact as his tools, and shows how to set up the tools, add partial hydration, lazy hydration, client entry file, configure Babel for Preact and bundle Preact with Rollup — from start to finish.

    With JAMStack used on large sites these days, a new performance consideration appeared: the build time . In fact, building out even thousands of pages with every new deploy can take minutes, so it's promising to see incremental builds in Gatsby which improve build times by 60 times , with an integration into popular CMS solutions like WordPress, Contentful, Drupal, Netlify CMS and others.

    A flow chart showing User 1 on the top left and User 2 on the bottom left showing the process of incremental status re-generation
    Incremental static regeneration with Next.js. (Image credit: Prisma.io) (Large preview)

    Also, Next.js announced ahead-of-time and incremental static generation, which allows us to add new static pages at runtime and update existing pages after they've been already built, by re-rendering them in the background as traffic comes in.

    Need an even more lightweight approach? In his talk on Eleventy, Alpine and Tailwind: towards a lightweight Jamstack, Nicola Goutay explains the differences between CSR, SSR and everything-in-between, and shows how to use a more lightweight approach — along with a GitHub repo that shows the approach in practice.

  2. Consider using PRPL pattern and app shell architecture.
    Different frameworks will have different effects on performance and will require different strategies of optimization, so you have to clearly understand all of the nuts and bolts of the framework you'll be relying on. When building a web app, look into the PRPL pattern and application shell architecture. The idea is quite straightforward: Push the minimal code needed to get interactive for the initial route to render quickly, then use service worker for caching and pre-caching resources and then lazy-load routes that you need, asynchronously.
PRPL Pattern in the application shell architecture
PRPL stands for Pushing critical resource, Rendering initial route, Pre-caching remaining routes and Lazy-loading remaining routes on demand.
Application shell architecture
An application shell is the minimal HTML, CSS, and JavaScript powering a user interface.
  1. Have you optimized the performance of your APIs?
    APIs are communication channels for an application to expose data to internal and third-party applications via endpoints . When designing and building an API, we need a reasonable protocol to enable the communication between the server and third-party requests. Representational State Transfer ( REST ) is a well-established, logical choice: it defines a set of constraints that developers follow to make content accessible in a performant, reliable and scalable fashion. Web services that conform to the REST constraints, are called RESTful web services .

    As with good ol' HTTP requests, when data is retrieved from an API, any delay in server response will propagate to the end user, hence delaying rendering . When a resource wants to retrieve some data from an API, it will need to request the data from the corresponding endpoint. A component that renders data from several resources, such as an article with comments and author photos in each comment, may need several roundtrips to the server to fetch all the data before it can be rendered. Furthermore, the amount of data returned through REST is often more than what is needed to render that component.

    If many resources require data from an API, the API might become a performance bottleneck. GraphQL provides a performant solution to these issues. Per se, GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. Unlike REST, GraphQL can retrieve all data in a single request , and the response will be exactly what is required, without over or under -fetching data as it typically happens with REST.

    In addition, because GraphQL is using schema (metadata that tells how the data is structured), it can already organize data into the preferred structure, so, for example, with GraphQL, we could remove JavaScript code used for dealing with state management, producing a cleaner application code that runs faster on the client.

    If you want to get started with GraphQL or encounter performance issues, these articles might be quite helpful:

    • A GraphQL Primer: Why We Need A New Kind Of API by Eric Baer,
    • A GraphQL Primer: The Evolution Of API Design by Eric Baer,
    • Designing a GraphQL server for optimal performance by Leonardo Losoviz,
    • GraphQL performance explained by Wojciech Trocki.
Two examples of mobile interfaces for messages while using Redux/REST (left) and Apollo/GraphQL (right)
A difference between REST and GraphQL, illustrated via a conversation between Redux + REST on the left, an Apollo + GraphQL on the right. (Image source: Hacker Noon) (Large preview)
  1. Will you be using AMP or Instant Articles?
    Depending on the priorities and strategy of your organization, you might want to consider using Google's AMP or Facebook's Instant Articles or Apple's Apple News. You can achieve good performance without them, but AMP does provide a solid performance framework with a free content delivery network (CDN), while Instant Articles will boost your visibility and performance on Facebook.

    The seemingly obvious benefit of these technologies for users is guaranteed performance , so at times they might even prefer AMP-/Apple News/Instant Pages-links over "regular" and potentially bloated pages. For content-heavy websites that are dealing with a lot of third-party content, these options could potentially help speed up render times dramatically.

    Unless they don't. According to Tim Kadlec, for example, "AMP documents tend to be faster than their counterparts, but they don't necessarily mean a page is performant. AMP is not what makes the biggest difference from a performance perspective."

    A benefit for the website owner is obvious: discoverability of these formats on their respective platforms and increased visibility in search engines.

    Well, at least that's how it used to be. As AMP is no longer a requirement for Top Stories , publishers might be moving away from AMP to a traditional stack instead ( thanks, Barry! ).

    Still, you could build progressive web AMPs, too, by reusing AMPs as a data source for your PWA. Downside? Obviously, a presence in a walled garden places developers in a position to produce and maintain a separate version of their content, and in case of Instant Articles and Apple News without actual URLs (thanks Addy, Jeremy!) .

  2. Choose your CDN wisely.
    As mentioned above, depending on how much dynamic data you have, you might be able to "outsource" some part of the content to a static site generator, pushing it to a CDN and serving a static version from it, thus avoiding requests to the server. In fact, some of those generators are actually website compilers with many automated optimizations provided out of the box. As compilers add optimizations over time, the compiled output gets smaller and faster over time.

    Notice that CDNs can serve (and offload) dynamic content as well. So, restricting your CDN to static assets is not necessary. Double-check whether your CDN performs compression and conversion (eg image optimization and resizing at the edge), whether they provide support for servers workers, A/B testing, as well as edge-side includes, which assemble static and dynamic parts of pages at the CDN's edge (ie the server closest to the user), and other tasks. Also, check if your CDN supports HTTP over QUIC (HTTP/3).

    Katie Hempenius has written a fantastic guide to CDNs that provides insights on how to choose a good CDN , how to finetune it and all the little things to keep in mind when evaluating one. In general, it's a good idea to cache content as aggressively as possible and enable CDN performance features like Brotli, TLS 1.3, HTTP/2, and HTTP/3.

    Note : based on research by Patrick Meenan and Andy Davies, HTTP/2 prioritization is effectively broken on many CDNs, so be careful when choosing a CDN. Patrick has more details in his talk on HTTP/2 Prioritization ( thanks, Barry! ).

    CDNPerf preview of CDN names and query speed in ms
    CDNPerf measures query speed for CDNs by gathering and analyzing 300 million tests every day. (Visualização grande)

    When choosing a CDN, you can use these comparison sites with a detailed overview of their features:

    • Comparação de CDN, uma matriz de comparação de CDN para Cloudfront, Aazure, KeyCDN, Fastly, Verizon, Stackpach, Akamai e muitos outros.
    • O CDN Perf mede a velocidade de consulta para CDNs reunindo e analisando 300 milhões de testes todos os dias, com todos os resultados baseados em dados RUM de usuários de todo o mundo. Verifique também a comparação de desempenho de DNS e a comparação de desempenho de nuvem.
    • O CDN Planet Guides fornece uma visão geral das CDNs para tópicos específicos, como Servir obsoleto, Purge, Origin Shield, Prefetch e Compression.
    • Web Almanac: CDN Adoption and Usage fornece informações sobre os principais provedores de CDN, seu gerenciamento de RTT e TLS, tempo de negociação de TLS, adoção de HTTP/2 e outros. (Infelizmente, os dados são apenas de 2019).

Otimizações de recursos

  1. Use Brotli para compactação de texto simples.
    Em 2015, o Google lançou o Brotli, um novo formato de dados sem perdas de código aberto, que agora é compatível com todos os navegadores modernos. A biblioteca de código aberto Brotli, que implementa um codificador e decodificador para Brotli, possui 11 níveis de qualidade predefinidos para o codificador, com um nível de qualidade mais alto exigindo mais CPU em troca de uma melhor taxa de compactação. A compactação mais lenta levará a taxas de compactação mais altas, mas ainda assim, o Brotli descompacta rapidamente. Vale a pena notar que o Brotli com o nível de compactação 4 é menor e compacta mais rápido que o Gzip.

    Na prática, o Brotli parece ser muito mais eficaz que o Gzip. As opiniões e experiências diferem, mas se o seu site já estiver otimizado com Gzip, você pode esperar pelo menos melhorias de um dígito e, na melhor das hipóteses, melhorias de dois dígitos na redução de tamanho e nos tempos de FCP. Você também pode estimar a economia de compactação do Brotli para o seu site.

    Os navegadores aceitarão o Brotli somente se o usuário estiver visitando um site por HTTPS. O Brotli é amplamente suportado e muitos CDNs o suportam (Akamai, Netlify Edge, AWS, KeyCDN, Fastly (atualmente apenas como pass-through), Cloudflare, CDN77) e você pode habilitar o Brotli mesmo em CDNs que ainda não o suportam (com um trabalhador de serviço).

    O problema é que, como a compactação de todos os ativos com o Brotli em um alto nível de compactação é caro, muitos provedores de hospedagem não podem usá-lo em escala total apenas devido à enorme sobrecarga de custos que ele produz. Na verdade, no nível mais alto de compactação, o Brotli é tão lento que qualquer ganho potencial no tamanho do arquivo pode ser anulado pelo tempo que leva para o servidor começar a enviar a resposta enquanto espera para compactar dinamicamente o ativo. (Mas se você tiver tempo durante o tempo de compilação com compactação estática, é claro que as configurações de compactação mais altas são preferidas.)

    Uma comparação mostrada como um gráfico de bigodes mostrando vários métodos de compactação em três tempos de back-end diferentes: mínimo, médio e 90º percentil
    Comparação de tempos de back-end de vários métodos de compactação. Sem surpresa, o Brotli é mais lento que o gzip (por enquanto). (Visualização grande)

    Isso pode estar mudando embora. O formato de arquivo Brotli inclui um dicionário estático integrado e, além de conter várias strings em vários idiomas, também suporta a opção de aplicar várias transformações a essas palavras, aumentando sua versatilidade. Em sua pesquisa, Felix Hanau descobriu uma maneira de melhorar a compactação nos níveis 5 a 9 usando "um subconjunto mais especializado do dicionário do que o padrão" e contando com o cabeçalho Content-Type para informar ao compressor se ele deve usar um dicionário para HTML, JavaScript ou CSS. O resultado foi um "impacto de desempenho insignificante (1% a 3% a mais de CPU em comparação com 12% normalmente) ao compactar conteúdo da Web em altos níveis de compactação, usando uma abordagem de uso limitado de dicionário".

    Um gráfico de barras mostrando o ganho de compressão usando dicionários reduzidos Brotli no nível 5
    Com a abordagem de dicionário aprimorada, podemos compactar ativos mais rapidamente em níveis de compactação mais altos, usando apenas 1% a 3% a mais de CPU. Normalmente, o nível de compactação 6 sobre 5 aumentaria o uso da CPU em até 12%. (Visualização grande)

    Além disso, com a pesquisa de Elena Kirilenko, podemos obter uma recompressão Brotli rápida e eficiente usando artefatos de compressão anteriores. De acordo com Elena, "uma vez que temos um ativo compactado via Brotli, e estamos tentando compactar conteúdo dinâmico dinamicamente, onde o conteúdo se assemelha ao conteúdo disponível para nós antes do tempo, podemos obter melhorias significativas nos tempos de compactação. "

    Quantas vezes é o caso? Por exemplo, com entrega de subconjuntos de bundles JavaScript (por exemplo, quando partes do código já estão armazenadas em cache no cliente ou com bundle dinâmico servindo com WebBundles). Ou com HTML dinâmico baseado em modelos conhecidos antecipadamente ou fontes WOFF2 subconjuntos dinamicamente . De acordo com Elena, podemos obter 5,3% de melhoria na compactação e 39% na velocidade de compactação ao remover 10% do conteúdo e 3,2% melhores taxas de compactação e compactação 26% mais rápida, ao remover 50% do conteúdo.

    A compactação Brotli está melhorando, portanto, se você puder contornar o custo de compactar dinamicamente ativos estáticos, definitivamente vale a pena o esforço. Escusado será dizer que o Brotli pode ser usado para qualquer carga útil de texto simples — HTML, CSS, SVG, JavaScript, JSON e assim por diante.

    Observação : no início de 2021, aproximadamente 60% das respostas HTTP são entregues sem compactação baseada em texto, com 30,82% compactando com Gzip e 9,1% compactando com Brotli (tanto em dispositivos móveis quanto em desktops). Por exemplo, 23,4% das páginas Angular não são compactadas (via gzip ou Brotli). No entanto, muitas vezes, ativar a compactação é uma das vitórias mais fáceis para melhorar o desempenho com um simples toque de um botão.

    A estratégia? Pré-compacte ativos estáticos com Brotli+Gzip no nível mais alto e compactar HTML (dinâmico) dinamicamente com Brotli no nível 4–6. Certifique-se de que o servidor lide com a negociação de conteúdo para Brotli ou Gzip corretamente.

Um gráfico de barras mostrando os algoritmos de compactação para solicitações HTTP de acordo com o relatório Web Almanax 2020
Dos recursos atendidos compactados em 2020, 22,59% são compactados com Brotli. Cerca de 77,39% são compactados com gzip. (Fonte da imagem: Web Almanac: Compression) (Visualização grande)
  1. Usamos carregamento de mídia adaptável e dicas do cliente?
    Está vindo da terra das notícias antigas, mas é sempre um bom lembrete usar imagens responsivas com srcset , sizes e o elemento <picture> . Especialmente para sites com uma pegada de mídia pesada, podemos dar um passo adiante com carregamento de mídia adaptável (neste exemplo React + Next.js), oferecendo experiência leve para redes lentas e dispositivos com pouca memória e experiência completa para rede rápida e alta -dispositivos de memória. No contexto do React, podemos alcançá-lo com dicas do cliente no servidor e ganchos adaptáveis ​​de reação no cliente.

    O futuro das imagens responsivas pode mudar drasticamente com a adoção mais ampla das dicas do cliente. As dicas do cliente são campos de cabeçalho de solicitação HTTP, por exemplo, DPR , Viewport-Width , Width , Save-Data , Accept (para especificar preferências de formato de imagem) e outros. Eles devem informar o servidor sobre as especificidades do navegador do usuário, tela, conexão etc.

    Como resultado, o servidor pode decidir como preencher o layout com imagens de tamanho apropriado e servir apenas essas imagens nos formatos desejados. Com as dicas do cliente, movemos a seleção de recursos da marcação HTML para a negociação de solicitação-resposta entre o cliente e o servidor.

    Uma ilustração mostrando como o serviço de mídia adaptável pode ser usado enviando diferentes resoluções aos usuários, dependendo da capacidade da rede
    Mídia adaptável servindo em uso. Enviamos um placeholder com texto para usuários que estão offline, uma imagem de baixa resolução para usuários de 2G, uma imagem de alta resolução para usuários de 3G e um vídeo HD para usuários de 4G. Via Carregando páginas da web rapidamente em um telefone comum de US$ 20. (Visualização grande)

    Como Ilya Grigorik observou há algum tempo, as dicas do cliente completam o quadro – elas não são uma alternativa para imagens responsivas. "O elemento <picture> fornece o controle de direção de arte necessário na marcação HTML. As dicas do cliente fornecem anotações nas solicitações de imagem resultantes que permitem a automação da seleção de recursos. O Service Worker fornece recursos completos de gerenciamento de solicitação e resposta no cliente."

    Um service worker pode, por exemplo, anexar novos valores de cabeçalhos de dicas do cliente à solicitação, reescrever a URL e apontar a solicitação de imagem para um CDN, adaptar a resposta com base na conectividade e nas preferências do usuário etc. Isso vale não apenas para ativos de imagem, mas para praticamente todos os outros pedidos também.

    Para clientes que suportam dicas de clientes, pode-se medir 42% de economia de bytes em imagens e 1 MB+ menos bytes para 70º percentil. Na Smashing Magazine, também pudemos medir uma melhora de 19 a 32%. As dicas do cliente são suportadas em navegadores baseados no Chromium, mas ainda estão sendo consideradas no Firefox.

    No entanto, se você fornecer a marcação normal de imagens responsivas e a tag <meta> para Client Hints, um navegador de suporte avaliará a marcação de imagens responsivas e solicitará a fonte de imagem apropriada usando os cabeçalhos HTTP Client Hints.

  2. Usamos imagens responsivas para imagens de fundo?
    Certamente devemos! Com image-set , agora suportado no Safari 14 e na maioria dos navegadores modernos, exceto Firefox, também podemos fornecer imagens de plano de fundo responsivas:

    background-image: url("fallback.jpg"); background-image: image-set( "photo-small.jpg" 1x, "photo-large.jpg" 2x, "photo-print.jpg" 600dpi);

    Basicamente, podemos fornecer condicionalmente imagens de fundo de baixa resolução com um descritor de 1x e imagens de alta resolução com um descritor de 2x e até mesmo uma imagem com qualidade de impressão com um descritor de 600dpi . Mas cuidado: os navegadores não fornecem nenhuma informação especial sobre imagens de fundo para tecnologia assistiva, então o ideal é que essas fotos sejam meramente decorativas.

  3. Usamos WebP?
    A compactação de imagem é frequentemente considerada uma vitória rápida, mas ainda é muito subutilizada na prática. É claro que as imagens não bloqueiam a renderização, mas contribuem fortemente para pontuações ruins de LCP e, muitas vezes, são muito pesadas e grandes para o dispositivo em que estão sendo consumidas.

    Então, no mínimo, poderíamos explorar o uso do formato WebP para nossas imagens. Na verdade, a saga WebP está chegando ao fim no ano passado com a Apple adicionando suporte para WebP no Safari 14. Então, após muitos anos de discussões e debates, a partir de hoje, o WebP é suportado em todos os navegadores modernos. Assim, podemos servir imagens WebP com o elemento <picture> e um fallback JPEG, se necessário (consulte o trecho de código de Andreas Bovens) ou usando negociação de conteúdo (usando cabeçalhos Accept ).

    O WebP também tem suas desvantagens . Embora os tamanhos de arquivo de imagem WebP sejam comparados aos equivalentes Guetzli e Zopfli, o formato não suporta renderização progressiva como JPEG, e é por isso que os usuários podem ver a imagem finalizada mais rapidamente com um bom e velho JPEG, embora as imagens WebP possam estar ficando mais rápidas pela rede. Com o JPEG, podemos fornecer uma experiência de usuário "decente" com metade ou até um quarto dos dados e carregar o restante mais tarde, em vez de ter uma imagem meio vazia como no caso do WebP.

    Sua decisão dependerá do que você busca: com WebP, você reduzirá a carga útil e, com JPEG, melhorará o desempenho percebido. Você pode aprender mais sobre WebP na palestra WebP Rewind de Pascal Massimino, do Google.

    Para conversão para WebP, você pode usar o WebP Converter, cwebp ou libwebp. Ire Aderinokun também tem um tutorial muito detalhado sobre como converter imagens para WebP – e Josh Comeau também em seu artigo sobre como adotar formatos de imagem modernos.

    Um slide usado para a palestra de Pascal Massimino intitulada Image ready: webp rewind
    Uma palestra completa sobre WebP: WebP Rewind de Pascal Massimino. (Visualização grande)

    O Sketch oferece suporte nativo ao WebP e as imagens do WebP podem ser exportadas do Photoshop usando um plug-in WebP para o Photoshop. Mas outras opções também estão disponíveis.

    Se você estiver usando WordPress ou Joomla, existem extensões para ajudá-lo a implementar facilmente o suporte para WebP, como Optimus e Cache Enabler para WordPress e a própria extensão suportada do Joomla (via Cody Arsenault). Você também pode abstrair o elemento <picture> com React, styled components ou gatsby-image.

    Ah – plug sem vergonha! — Jeremy Wagner até publicou um livro sensacional sobre WebP que você pode querer verificar se estiver interessado em tudo sobre WebP.

  4. Usamos AVIF?
    Você já deve ter ouvido a grande notícia: o AVIF chegou. É um novo formato de imagem derivado dos quadros-chave do vídeo AV1. É um formato aberto e livre de royalties que suporta compactação com e sem perdas, animação, canal alfa com perdas e pode lidar com linhas nítidas e cores sólidas (o que era um problema com o JPEG), proporcionando melhores resultados em ambos.

    Na verdade, em comparação com WebP e JPEG, o AVIF tem um desempenho significativamente melhor , gerando economias de tamanho médio de arquivo de até 50% no mesmo DSSIM ((dis)similaridade entre duas ou mais imagens usando um algoritmo que aproxima a visão humana). De fato, em seu post completo sobre como otimizar o carregamento de imagens, Malte Ubl observa que o AVIF "supera consistentemente o JPEG de uma maneira muito significativa. Isso é diferente do WebP, que nem sempre produz imagens menores que o JPEG e pode realmente ser uma rede perda por falta de suporte para carregamento progressivo."

    Um trecho de código mostrando AVIF como aprimoramento progressivo
    Podemos usar o AVIF como um aprimoramento progressivo, fornecendo WebP ou JPEG ou PNG para navegadores mais antigos. (Grande visualização). Veja a visualização de texto simples abaixo.

    Ironicamente, o AVIF pode ter um desempenho ainda melhor do que os SVGs grandes, embora, é claro, não deva ser visto como um substituto para os SVGs. É também um dos primeiros formatos de imagem a oferecer suporte a cores HDR; oferecendo maior brilho, profundidade de bits de cor e gamas de cores. A única desvantagem é que atualmente o AVIF não suporta decodificação de imagem progressiva (ainda?)

    O AVIF é atualmente suportado no Chrome, Firefox e Opera, e o suporte no Safari deve chegar em breve (já que a Apple é membro do grupo que criou o AV1).

    Qual é a melhor maneira de veicular imagens hoje em dia ? Para ilustrações e imagens vetoriais, o SVG (comprimido) é sem dúvida a melhor escolha. Para fotos, usamos métodos de negociação de conteúdo com o elemento picture . Se houver suporte para AVIF, enviamos uma imagem AVIF; se não for o caso, voltamos primeiro ao WebP e, se o WebP também não for suportado, alternamos para JPEG ou PNG como fallback (aplicando condições @media se necessário):

    <picture> <source type="image/avif"> <source type="image/webp"> <img src="image.jpg" alt="Photo" width="450" height="350"> </picture>

    Francamente, é mais provável que usemos algumas condições dentro do elemento de picture :

    <picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>
    <picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>

    Você pode ir ainda mais longe trocando imagens animadas por imagens estáticas para clientes que optam por menos movimento com prefers-reduced-motion :

    <picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>
    <picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>

    Ao longo dos dois meses, o AVIF ganhou bastante força:

    • Podemos testar fallbacks WebP/AVIF no painel Rendering no DevTools.
    • Podemos usar Squoosh, AVIF.io e libavif para codificar, decodificar, compactar e converter arquivos AVIF.
    • Podemos usar o componente AVIF Preact de Jake Archibald que decodifica um arquivo AVIF em um trabalhador e exibe o resultado em uma tela,
    • Para fornecer AVIF apenas para navegadores compatíveis, podemos usar um plug-in PostCSS junto com um script 315B para usar AVIF em suas declarações CSS.
    • Podemos entregar progressivamente novos formatos de imagem com CSS e Cloudlare Workers para alterar dinamicamente o documento HTML retornado, inferindo informações do cabeçalho de accept e, em seguida, adicionar as webp/avif etc. conforme apropriado.
    • O AVIF já está disponível no Cloudinary (com limites de uso), o Cloudflare suporta AVIF no redimensionamento de imagem e você pode habilitar o AVIF com cabeçalhos AVIF personalizados no Netlify.
    • Quando se trata de animação, o AVIF funciona tão bem quanto o <img src=mp4> do Safari, superando o GIF e o WebP em geral, mas o MP4 ainda tem um desempenho melhor.
    • Em geral, para animações, AVC1 (h264) > HVC1 > WebP > AVIF > GIF, assumindo que os navegadores baseados em Chromium sempre suportarão <img src=mp4> .
    • Você pode encontrar mais detalhes sobre AVIF na palestra AVIF for Next Generation Image Coding, de Aditya Mavlankar, da Netflix, e The AVIF Image Format, de Kornel Lesinski, da Cloudflare.
    • Uma ótima referência para tudo AVIF: o post abrangente de Jake Archibald sobre AVIF chegou.

    Então é o futuro AVIF ? Jon Sneyers discorda: o AVIF tem um desempenho 60% pior que o JPEG XL, outro formato gratuito e aberto desenvolvido pelo Google e Cloudinary. Na verdade, o JPEG XL parece ter um desempenho muito melhor em todos os aspectos. No entanto, o JPEG XL ainda está apenas nos estágios finais de padronização, e ainda não funciona em nenhum navegador. (Para não confundir com o JPEG-XR da Microsoft vindo do bom e velho Internet Explorer 9 vezes).

Gerador de pontos de interrupção de imagem responsiva
O Responsive Image Breakpoints Generator automatiza a geração de imagens e marcação.
  1. Os JPEG/PNG/SVGs estão devidamente otimizados?
    Quando você estiver trabalhando em uma página de destino na qual é fundamental que uma imagem principal carregue incrivelmente rápido, certifique-se de que os JPEGs sejam progressivos e compactados com mozJPEG (que melhora o tempo de renderização inicial manipulando os níveis de varredura) ou Guetzli, o software de código aberto do Google codificador com foco no desempenho perceptivo e utilizando aprendizados de Zopfli e WebP. A única desvantagem: tempos de processamento lentos (um minuto de CPU por megapixel).

    Para PNG, podemos usar Pingo, e para SVG, podemos usar SVGO ou SVGOMG. E se você precisar visualizar e copiar ou baixar rapidamente todos os recursos SVG de um site, o svg-grabber também pode fazer isso por você.

    Cada artigo de otimização de imagem afirmaria isso, mas sempre vale a pena mencionar manter os ativos vetoriais limpos e firmes. Certifique-se de limpar ativos não utilizados, remover metadados desnecessários e reduzir o número de pontos de caminho no trabalho artístico (e, portanto, no código SVG). ( Obrigado, Jeremias! )

    Existem também ferramentas online úteis disponíveis:

    • Use o Squoosh para compactar, redimensionar e manipular imagens nos níveis de compactação ideais (com ou sem perdas),
    • Use o Guetzli.it para compactar e otimizar imagens JPEG com o Guetzli, que funciona bem para imagens com bordas nítidas e cores sólidas (mas pode ser um pouco mais lento)).
    • Use o Gerador de pontos de interrupção de imagem responsiva ou um serviço como Cloudinary ou Imgix para automatizar a otimização de imagem. Além disso, em muitos casos, usar srcset e sizes trará benefícios significativos.
    • Para verificar a eficiência de sua marcação responsiva, você pode usar o imaging-heap, uma ferramenta de linha de comando que mede a eficiência entre os tamanhos da janela de visualização e as proporções de pixels do dispositivo.
    • Você pode adicionar compactação automática de imagem aos seus fluxos de trabalho do GitHub, para que nenhuma imagem atinja a produção sem compactação. A ação usa mozjpeg e libvips que funcionam com PNGs e JPGs.
    • Para otimizar o armazenamento internamente, você pode usar o novo formato Lepton do Dropbox para compactar JPEGs sem perdas em uma média de 22%.
    • Use BlurHash se quiser mostrar uma imagem de espaço reservado antecipadamente. BlurHash pega uma imagem e fornece uma string curta (apenas 20 a 30 caracteres!) que representa o espaço reservado para essa imagem. A string é curta o suficiente para que possa ser facilmente adicionada como um campo em um objeto JSON.
    Uma comparação de uma interface sem espaços reservados de imagem à esquerda e com espaços reservados mostrados à direita
    BlurHash é uma representação pequena e compacta de um espaço reservado para uma imagem. (Visualização grande)

    Às vezes, otimizar imagens por si só não resolve. Para melhorar o tempo necessário para iniciar a renderização de uma imagem crítica, carregue lentamente as imagens menos importantes e adie o carregamento de qualquer script após as imagens críticas já terem sido renderizadas. A maneira mais à prova de balas é o carregamento lento híbrido, quando utilizamos o carregamento lento e o carregamento lento nativos, uma biblioteca que detecta quaisquer alterações de visibilidade desencadeadas pela interação do usuário (com IntersectionObserver, que exploraremos mais tarde). Além disso:

    • Considere pré-carregar imagens críticas, para que um navegador não as descubra tarde demais. Para imagens de fundo, se você quiser ser ainda mais agressivo do que isso, você pode adicionar a imagem como uma imagem normal com <img src> e então ocultá-la da tela.
    • Considere Trocar Imagens com o Atributo Tamanhos especificando diferentes dimensões de exibição de imagem dependendo das consultas de mídia, por exemplo, para manipular sizes para trocar fontes em um componente de lupa.
    • Revise as inconsistências de download de imagens para evitar downloads inesperados de imagens de primeiro e segundo plano. Cuidado com as imagens que são carregadas por padrão, mas podem nunca ser exibidas - por exemplo, em carrosséis, acordeões e galerias de imagens.
    • Certifique-se de sempre definir width e height nas imagens. Fique atento à propriedade aspect-ratio no CSS e ao atributo intrinsicsize que nos permitirá definir proporções e dimensões para imagens, para que o navegador possa reservar um slot de layout predefinido antecipadamente para evitar saltos de layout durante o carregamento da página.
    Uma captura de tela do código mostrando elementos de preenchimento superior e proporção em uso em um editor
    Deve ser apenas uma questão de semanas ou meses agora, com o desembarque da proporção nos navegadores. Já no Safari Technical Preview 118. Atualmente atrás da bandeira no Firefox e Chrome. (Visualização grande)

    Se você se sentir aventureiro, pode cortar e reorganizar os fluxos HTTP/2 usando Edge workers, basicamente um filtro em tempo real que vive na CDN, para enviar imagens mais rapidamente pela rede. Os trabalhadores de borda usam fluxos de JavaScript que usam partes que você pode controlar (basicamente são JavaScript executados na borda da CDN que podem modificar as respostas de streaming), para que você possa controlar a entrega de imagens.

    Com um service worker, é tarde demais, pois você não pode controlar o que está acontecendo, mas funciona com Edge workers. Assim, você pode usá-los em cima de JPEGs estáticos salvos progressivamente para uma página de destino específica.

    Uma captura de tela da ferramenta de linha de comando do heap de imagem mostrando uma tabela com vários tamanhos de janela de visualização e proporções de pixels do dispositivo
    Uma saída de amostra por heap de imagens, uma ferramenta de linha de comando que mede a eficiência entre os tamanhos das portas de visualização e as proporções de pixels do dispositivo. (Fonte da imagem) (Visualização grande)

    Não esta bom o suficiente? Bem, você também pode melhorar o desempenho percebido para imagens com a técnica de várias imagens de fundo. Lembre-se de que brincar com contraste e desfocar detalhes desnecessários (ou remover cores) também pode reduzir o tamanho do arquivo. Ah, você precisa ampliar uma foto pequena sem perder a qualidade? Considere usar Letsenhance.io.

    Essas otimizações até agora cobrem apenas o básico. Addy Osmani publicou um guia muito detalhado sobre Essential Image Optimization, que aprofunda os detalhes de compactação de imagem e gerenciamento de cores. Por exemplo, você pode desfocar partes desnecessárias da imagem (aplicando um filtro de desfoque gaussiano a elas) para reduzir o tamanho do arquivo e, eventualmente, pode até começar a remover cores ou transformar a imagem em preto e branco para reduzir ainda mais o tamanho . Para imagens de fundo, exportar fotos do Photoshop com qualidade de 0 a 10% também pode ser absolutamente aceitável.

    Na Smashing Magazine, usamos o postfix -opt para nomes de imagens — por exemplo, brotli-compression-opt.png ; sempre que uma imagem contém esse postfix, todos na equipe sabem que a imagem já foi otimizada.

    Ah, e não use JPEG-XR na web - "o processamento de decodificação JPEG-XRs no lado do software na CPU anula e até supera o impacto potencialmente positivo da economia de tamanho de byte, especialmente no contexto de SPAs" (não para misturar com o JPEG XL da Cloudinary/Google).

Substituindo GIFs animados pelo elemento de vídeo com mais de 80% de economia
Addy Osmani recomenda substituir GIFs animados por vídeos inline em loop. A diferença no tamanho do arquivo é perceptível (economia de 80%). (Visualização grande)
  1. Os vídeos estão devidamente otimizados?
    Cobrimos imagens até agora, mas evitamos uma conversa sobre bons e velhos GIFs. Apesar do nosso amor por GIFs, é realmente a hora de abandoná-los para sempre (pelo menos em nossos sites e aplicativos). Em vez de carregar GIFs animados pesados ​​que afetam tanto o desempenho de renderização quanto a largura de banda, é uma boa ideia alternar para WebP animado (com GIF sendo um substituto) ou substituí-los por vídeos HTML5 em loop.

    Ao contrário das imagens, os navegadores não pré-carregam o conteúdo <video> , mas os vídeos HTML5 tendem a ser muito mais leves e menores que os GIFs. Não é uma opção? Bem, pelo menos podemos adicionar compressão com perdas a GIFs com Lossy GIF, gifsicle ou giflossy.

    Testes de Colin Bendell mostram que vídeos inline dentro de tags img no Safari Technology Preview são exibidos pelo menos 20 vezes mais rápido e decodificam 7 vezes mais rápido que o equivalente GIF, além de serem uma fração do tamanho do arquivo. No entanto, não é compatível com outros navegadores.

    Na terra das boas notícias, os formatos de vídeo vêm avançando massivamente ao longo dos anos. Por muito tempo, esperávamos que o WebM se tornasse o formato para governar todos eles, e o WebP (que é basicamente uma imagem estática dentro do contêiner de vídeo WebM) se tornaria um substituto para formatos de imagem datados. De fato, o Safari agora oferece suporte ao WebP, mas apesar do WebP e do WebM ganharem suporte nos dias de hoje, o avanço realmente não aconteceu.

    Ainda assim, poderíamos usar o WebM para a maioria dos navegadores modernos:

    <!-- By Houssein Djirdeh. https://web.dev/replace-gifs-with-videos/ --> <!-- A common scenartio: MP4 with a WEBM fallback. --> <video autoplay loop muted playsinline> <source src="my-animation.webm" type="video/webm"> <source src="my-animation.mp4" type="video/mp4"> </video>

    Mas talvez pudéssemos revisitá-lo completamente. Em 2018, a Alliance of Open Media lançou um novo formato de vídeo promissor chamado AV1 . O AV1 tem compactação semelhante ao codec H.265 (a evolução do H.264), mas ao contrário deste último, o AV1 é gratuito. O preço da licença H.265 levou os fornecedores de navegadores a adotar um AV1 com desempenho comparável: o AV1 (assim como o H.265) compacta duas vezes melhor que o WebM .

    Logotipo AV1 2018
    O AV1 tem boas chances de se tornar o padrão definitivo para vídeo na web. (Crédito da imagem: Wikimedia.org) (Visualização grande)

    Na verdade, a Apple atualmente usa o formato HEIF e HEVC (H.265), e todas as fotos e vídeos no iOS mais recente são salvos nesses formatos, não em JPEG. Embora o HEIF e o HEVC (H.265) não estejam devidamente expostos à web (ainda?), o AV1 está – e está ganhando suporte para navegadores. Portanto, adicionar a fonte AV1 em sua tag <video> é razoável, pois todos os fornecedores de navegadores parecem estar de acordo.

    Por enquanto, a codificação mais usada e suportada é H.264, servida por arquivos MP4, portanto, antes de servir o arquivo, certifique-se de que seus MP4s sejam processados ​​com uma codificação multipass, desfocada com o efeito frei0r iirblur (se aplicável) e Os metadados do moov atom são movidos para o cabeçalho do arquivo, enquanto seu servidor aceita o serviço de byte. Boris Schapira fornece instruções exatas para o FFmpeg otimizar os vídeos ao máximo. É claro que fornecer o formato WebM como alternativa também ajudaria.

    Precisa começar a renderizar vídeos mais rapidamente, mas os arquivos de vídeo ainda são muito grandes ? Por exemplo, sempre que você tem um grande vídeo de fundo em uma página de destino? Uma técnica comum a ser usada é mostrar primeiro o primeiro quadro como uma imagem estática ou exibir um segmento de loop curto e altamente otimizado que pode ser interpretado como parte do vídeo e, então, sempre que o vídeo estiver armazenado em buffer o suficiente, comece a reproduzir o vídeo real. Doug Sillars escreveu um guia detalhado para o desempenho do vídeo em segundo plano que pode ser útil nesse caso. ( Obrigado, Guy Podjarny! ).

    Para o cenário acima, você pode fornecer imagens de pôster responsivas . Por padrão, os elementos de video permitem apenas uma imagem como pôster, o que não é necessariamente o ideal. Podemos usar o Responsive Video Poster, uma biblioteca JavaScript que permite usar diferentes imagens de pôster para diferentes telas, além de adicionar uma sobreposição de transição e controle total de estilo dos espaços reservados de vídeo.

    A pesquisa mostra que a qualidade do fluxo de vídeo afeta o comportamento do espectador. Na verdade, os espectadores começam a abandonar o vídeo se o atraso na inicialização exceder cerca de 2 segundos. Além desse ponto, um aumento de 1 segundo no atraso resulta em um aumento de aproximadamente 5,8% na taxa de abandono. Portanto, não é surpreendente que o tempo médio de início do vídeo seja de 12,8 segundos, com 40% dos vídeos com pelo menos 1 travamento e 20% pelo menos 2 segundos de reprodução de vídeo travada. Na verdade, as paralisações de vídeo são inevitáveis ​​no 3G, pois os vídeos são reproduzidos mais rapidamente do que a rede pode fornecer conteúdo.

    Então, qual é a solução? Normalmente, os dispositivos de tela pequena não conseguem lidar com os 720p e 1080p que estamos atendendo na área de trabalho. De acordo com Doug Sillars, podemos criar versões menores de nossos vídeos e usar Javascript para detectar a origem de telas menores para garantir uma reprodução rápida e suave nesses dispositivos. Alternativamente, podemos usar streaming de vídeo. Os fluxos de vídeo HLS fornecerão um vídeo de tamanho apropriado para o dispositivo - abstraindo a necessidade de criar vídeos diferentes para telas diferentes. Ele também negociará a velocidade da rede e adaptará a taxa de bits do vídeo à velocidade da rede que você está usando.

    Para evitar o desperdício de largura de banda, só poderíamos adicionar a fonte de vídeo para dispositivos que realmente reproduzam bem o vídeo. Como alternativa, podemos remover completamente o atributo de autoplay da tag de video e usar JavaScript para inserir a autoplay em telas maiores. Além disso, precisamos adicionar preload="none" no video para informar ao navegador para não baixar nenhum dos arquivos de vídeo até que ele realmente precise do arquivo:

    <!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>

    Em seguida, podemos segmentar especificamente navegadores que realmente suportam AV1:

    <!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08"> <source src="video.hevc.mp4" type="video/mp4; codecs=hevc"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>

    Em seguida, poderíamos adicionar novamente a autoplay em um determinado limite (por exemplo, 1000px):

    /* By Doug Sillars. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ */ <script> window.onload = addAutoplay(); var videoLocation = document.getElementById("hero-video"); function addAutoplay() { if(window.innerWidth > 1000){ videoLocation.setAttribute("autoplay",""); }; } </script>
    Um gráfico de barras mostrando pequenos tme (ms) por dispositivo e velocidade de rede, incluindo 3G, Cable, LTE e Native em Alcatel 1X, Moto G, Moto G4, MotoE, Nexus 5 e OnePlus 5
    Número de paradas por dispositivo e velocidade da rede. Dispositivos mais rápidos em redes mais rápidas praticamente não têm paradas. De acordo com a pesquisa de Doug Sillars. (Visualização grande)

    O desempenho de reprodução de vídeo é uma história por si só, e se você quiser mergulhar em detalhes, dê uma olhada em outra série de Doug Sillars sobre o estado atual das práticas recomendadas de entrega de vídeo e vídeo que inclui detalhes sobre métricas de entrega de vídeo , pré-carregamento de vídeo, compressão e streaming. Por fim, você pode verificar o quão lento ou rápido será o seu streaming de vídeo com Stream or Not.

Guia completo de Zach Leatherman para estratégias de carregamento de fontes mostrado como um gráfico de mapa mental
O Guia Abrangente de Estratégias de Carregamento de Fontes de Zach Leatherman oferece uma dúzia de opções para uma melhor entrega de fontes da web.
  1. A entrega de fontes da web é otimizada?
    The first question that's worth asking is if we can get away with using UI system fonts in the first place — we just need to make sure to double check that they appear correctly on various platforms. If it's not the case, chances are high that the web fonts we are serving include glyphs and extra features and weights that aren't being used. We can ask our type foundry to subset web fonts or if we are using open-source fonts, subset them on our own with Glyphhanger or Fontsquirrel. We can even automate our entire workflow with Peter Muller's subfont, a command line tool that statically analyses your page in order to generate the most optimal web font subsets, and then inject them into our pages.

    WOFF2 support is great, and we can use WOFF as fallback for browsers that don't support it — or perhaps legacy browsers could be served system fonts. There are many, many, many options for web font loading, and we can choose one of the strategies from Zach Leatherman's "Comprehensive Guide to Font-Loading Strategies," (code snippets also available as Web font loading recipes).

    Probably the better options to consider today are Critical FOFT with preload and "The Compromise" method. Both of them use a two-stage render for delivering web fonts in steps — first a small supersubset required to render the page fast and accurately with the web font, and then load the rest of the family async. The difference is that "The Compromise" technique loads polyfill asynchronously only if font load events are not supported, so you don't need to load the polyfill by default. Need a quick win? Zach Leatherman has a quick 23-min tutorial and case study to get your fonts in order.

    In general, it might be a good idea to use the preload resource hint to preload fonts, but in your markup include the hints after the link to critical CSS and JavaScript. With preload , there is a puzzle of priorities, so consider injecting rel="preload" elements into the DOM just before the external blocking scripts. According to Andy Davies, "resources injected using a script are hidden from the browser until the script executes, and we can use this behaviour to delay when the browser discovers the preload hint." Otherwise, font loading will cost you in the first render time.

    A screenshot of slide 93 showing two example of images with a title next to them saying ‘Metrics prioritization: preload one of each family’
    When everything is critical, nothing is critical. preload only one or a maximum of two fonts of each family. (Image credit: Zach Leatherman – slide 93) (Large preview)

    It's a good idea to be selective and choose files that matter most, eg the ones that are critical for rendering or that would help you avoiding visible and disruptive text reflows. In general, Zach advises to preload one or two fonts of each family — it also makes sense to delay some font loading if they are less critical.

    It has become quite common to use local() value (which refers to a local font by name) when defining a font-family in the @font-face rule:

     /* Warning! Not a good idea! */ @font-face { font-family: Open Sans; src: local('Open Sans Regular'), local('OpenSans-Regular'), url('opensans.woff2') format ('woff2'), url('opensans.woff') format('woff'); }

    The idea is reasonable: some popular open-source fonts such as Open Sans are coming pre-installed with some drivers or apps, so if the font is available locally, the browser doesn't need to download the web font and can display the local font immediately. As Bram Stein noted, "though a local font matches the name of a web font, it most likely isn't the same font . Many web fonts differ from their "desktop" version. The text might be rendered differently, some characters may fall back to other fonts, OpenType features can be missing entirely, or the line height may be different."

    Also, as typefaces evolve over time, the locally installed version might be very different from the web font, with characters looking very different. So, according to Bram, it's better to never mix locally installed fonts and web fonts in @font-face rules. Google Fonts has followed suit by disabling local() on the CSS results for all users, other than Android requests for Roboto.

    Nobody likes waiting for the content to be displayed. With the font-display CSS descriptor, we can control the font loading behavior and enable content to be readable immediately (with font-display: optional ) or almost immediately (with a timeout of 3s, as long as the font gets successfully downloaded — with font-display: swap ). (Well, it's a bit more complicated than that.)

    However, if you want to minimize the impact of text reflows, we could use the Font Loading API (supported in all modern browsers). Specifically that means for every font, we'd creata a FontFace object, then try to fetch them all, and only then apply them to the page. This way, we group all repaints by loading all fonts asynchronously, and then switch from fallback fonts to the web font exactly once. Take a look at Zach's explanation, starting at 32:15, and the code snippet):

    /* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));
    /* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));

    To initiate a very early fetch of the fonts with Font Loading API in use, Adrian Bece suggests to add a non-breaking space nbsp; at the top of the body , and hide it visually with aria-visibility: hidden and a .hidden class:

    <body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>
    <body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>

    This goes along with CSS that has different font families declared for different states of loading, with the change triggered by Font Loading API once the fonts have successfully loaded:

    body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }
    body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }

    If you ever wondered why despite all your optimizations, Lighthouse still suggests to eliminate render-blocking resources (fonts), in the same article Adrian Bece provides a few techniques to make Lighthouse happy, along with a Gatsby Omni Font Loader, a performant asynchronous font loading and Flash Of Unstyled Text (FOUT) handling plugin for Gatsby.

    Now, many of us might be using a CDN or a third-party host to load web fonts from. In general, it's always better to self-host all your static assets if you can, so consider using google-webfonts-helper, a hassle-free way to self-host Google Fonts. And if it's not possible, you can perhaps proxy the Google Font files through the page origin.

    It's worth noting though that Google is doing quite a bit of work out of the box, so a server might need a bit of tweaking to avoid delays ( thanks, Barry! )

    This is quite important especially as since Chrome v86 (released October 2020), cross-site resources like fonts can't be shared on the same CDN anymore — due to the partitioned browser cache. This behavior was a default in Safari for years.

    But if it's not possible at all, there is a way to get to the fastest possible Google Fonts with Harry Roberts' snippet:

    <!-- By Harry Roberts. https://csswizardry.com/2020/05/the-fastest-google-fonts/ - 1. Preemptively warm up the fonts' origin. - 2. Initiate a high-priority, asynchronous fetch for the CSS file. Works in - most modern browsers. - 3. Initiate a low-priority, asynchronous fetch that gets applied to the page - only after it's arrived. Works in all browsers with JavaScript enabled. - 4. In the unlikely event that a visitor has intentionally disabled - JavaScript, fall back to the original method. The good news is that, - although this is a render-blocking request, it can still make use of the - preconnect which makes it marginally faster than the default. --> <!-- [1] --> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <!-- [2] --> <link rel="preload" as="style" href="$CSS&display=swap" /> <!-- [3] --> <link rel="stylesheet" href="$CSS&display=swap" media="print" onload="this.media='all'" /> <!-- [4] --> <noscript> <link rel="stylesheet" href="$CSS&display=swap" /> </noscript>

    Harry's strategy is to pre-emptively warm up the fonts' origin first. Then we initiate a high-priority, asynchronous fetch for the CSS file. Afterwards, we initiate a low-priority, asynchronous fetch that gets applied to the page only after it's arrived (with a print stylesheet trick). Finally, if JavaScript isn't supported, we fall back to the original method.

    Ah, talking about Google Fonts: you can shave up to 90% of the size of Google Fonts requests by declaring only characters you need with &text . Plus, the support for font-display was added recently to Google Fonts as well, so we can use it out of the box.

    Uma palavra rápida de cautela embora. Se você usar font-display: optional , pode não ser ideal usar também o preload -carregamento, pois ele acionará essa solicitação de fonte da Web antecipadamente (causando congestionamento de rede se você tiver outros recursos de caminho crítico que precisam ser buscados). Use a pré- preconnect para solicitações de fontes cruzadas mais rápidas, mas tenha cuidado com o preload -carregamento, pois o pré-carregamento de fontes de uma origem diferente incorrerá em contenção de rede. Todas essas técnicas são abordadas nas receitas de carregamento de fontes da Web de Zach.

    Por outro lado, pode ser uma boa ideia desativar fontes da Web (ou pelo menos renderização de segundo estágio) se o usuário tiver ativado Reduzir movimento nas preferências de acessibilidade ou tiver optado pelo Modo de economia Save-Data ) , ou quando o usuário tem uma conectividade lenta (via API de informações de rede).

    Também podemos usar a consulta de mídia CSS prefers-reduced-data para não definir declarações de fonte se o usuário tiver optado pelo modo de economia de dados (há outros casos de uso também). A consulta de mídia basicamente exporia se o cabeçalho de solicitação Save-Data da extensão HTTP Client Hint estiver ativado/desativado para permitir o uso com CSS. Atualmente suportado apenas no Chrome e Edge por trás de um sinalizador.

    Métricas? Para medir o desempenho de carregamento da fonte da web, considere a métrica All Text Visible (o momento em que todas as fontes foram carregadas e todo o conteúdo é exibido em fontes da web), Time to Real Italics, bem como Web Font Reflow Count após a primeira renderização. Obviamente, quanto menores forem as duas métricas, melhor será o desempenho.

    E quanto a fontes variáveis , você pode perguntar? É importante observar que fontes variáveis ​​podem exigir uma consideração significativa de desempenho. Eles nos dão um espaço de design muito mais amplo para escolhas tipográficas, mas tem o custo de uma única solicitação serial em oposição a várias solicitações de arquivos individuais.

    Embora as fontes variáveis ​​reduzam drasticamente o tamanho geral do arquivo combinado dos arquivos de fonte, essa solicitação única pode ser lenta, bloqueando a renderização de todo o conteúdo de uma página. Portanto, subconfigurar e dividir a fonte em conjuntos de caracteres ainda é importante. No lado bom, porém, com uma fonte variável no lugar, obteremos exatamente um refluxo por padrão, portanto, nenhum JavaScript será necessário para agrupar repinturas.

    Agora, o que faria uma estratégia de carregamento de fonte da web à prova de balas ? Subconjunto de fontes e prepare-os para a renderização em 2 estágios, declare-os com um descritor font-display , use a API de carregamento de fontes para agrupar repinturas e armazenar fontes no cache de um service worker persistente. Na primeira visita, injete o pré-carregamento de scripts antes do bloqueio de scripts externos. Você pode recorrer ao Font Face Observer de Bram Stein, se necessário. E se você estiver interessado em medir o desempenho do carregamento de fontes, Andreas Marschke explora o rastreamento de desempenho com a API Font e a API UserTiming.

    Por fim, não se esqueça de incluir unicode-range para dividir uma fonte grande em fontes menores específicas do idioma e use o combinador de estilo de fonte de Monica Dinculescu para minimizar uma mudança brusca no layout, devido a discrepâncias de tamanho entre o fallback e o fontes da web.

    Como alternativa, para emular uma fonte da Web para uma fonte de fallback, podemos usar os descritores @font-face para substituir as métricas de fonte (demo, ativado no Chrome 87). (Observe que os ajustes são complicados com pilhas de fontes complicadas.)

    O futuro parece brilhante? Com o enriquecimento progressivo da fonte, eventualmente poderemos "baixar apenas a parte necessária da fonte em qualquer página e, para solicitações subsequentes dessa fonte, 'corrigir' dinamicamente o download original com conjuntos adicionais de glifos conforme necessário em páginas sucessivas pontos de vista", como explica Jason Pamental. A demonstração de transferência incremental já está disponível e está em andamento.

Otimizações de compilação

  1. Definimos nossas prioridades?
    É uma boa idéia saber com o que você está lidando primeiro. Faça um inventário de todos os seus ativos (JavaScript, imagens, fontes, scripts de terceiros e módulos "caros" na página, como carrosséis, infográficos complexos e conteúdo multimídia) e divida-os em grupos.

    Monte uma planilha . Defina a experiência básica básica para navegadores legados (ou seja, conteúdo central totalmente acessível), a experiência aprimorada para navegadores capazes (ou seja, uma experiência completa e enriquecida) e os extras (ativos que não são absolutamente necessários e podem ser carregados lentamente, como fontes da web, estilos desnecessários, scripts de carrossel, players de vídeo, widgets de mídia social, imagens grandes). Anos atrás, publicamos um artigo sobre "Melhorando o desempenho da revista Smashing", que descreve essa abordagem em detalhes.

    Ao otimizar o desempenho, precisamos refletir nossas prioridades. Carregue a experiência principal imediatamente, depois os aprimoramentos e os extras .

  2. Você usa módulos JavaScript nativos em produção?
    Lembre-se da boa e velha técnica de cortar a mostarda para enviar a experiência principal para navegadores legados e uma experiência aprimorada para navegadores modernos? Uma variante atualizada da técnica poderia usar ES2017+ <script type="module"> , também conhecido como padrão de módulo/nomodule (também introduzido por Jeremy Wagner como serviço diferencial ).

    A ideia é compilar e servir dois bundles JavaScript separados : o build “regular”, aquele com Babel-transforms e polyfills e servi-los apenas para navegadores legados que realmente precisam deles, e outro bundle (mesma funcionalidade) que não tem transformações ou polyfills.

    Como resultado, ajudamos a reduzir o bloqueio do encadeamento principal, reduzindo a quantidade de scripts que o navegador precisa processar. Jeremy Wagner publicou um artigo abrangente sobre o serviço diferencial e como configurá-lo em seu pipeline de compilação, desde a configuração do Babel, até quais ajustes você precisará fazer no Webpack, bem como os benefícios de fazer todo esse trabalho.

    Os scripts do módulo JavaScript nativo são adiados por padrão, portanto, enquanto a análise de HTML está acontecendo, o navegador baixará o módulo principal.

    Um exemplo mostrando como os módulos JavaScript nativos são adiados por padrão
    Os módulos JavaScript nativos são adiados por padrão. Praticamente tudo sobre módulos JavaScript nativos. (Visualização grande)

    Uma nota de aviso, porém: o padrão de módulo/nomódulo pode sair pela culatra em alguns clientes, então você pode querer considerar uma solução alternativa: o padrão de serviço diferencial menos arriscado de Jeremy que, no entanto, evita o scanner de pré-carregamento, o que pode afetar o desempenho de maneiras que talvez não antecipar. ( obrigado, Jeremias! )

    Na verdade, o Rollup oferece suporte a módulos como um formato de saída, para que possamos agrupar código e implantar módulos em produção. Parcel tem suporte a módulo no Parcel 2. Para Webpack, module-nomodule-plugin automatiza a geração de scripts de módulo/nomodule.

    Observação : vale a pena afirmar que a detecção de recursos por si só não é suficiente para tomar uma decisão informada sobre a carga útil a ser enviada para esse navegador. Por si só, não podemos deduzir a capacidade do dispositivo da versão do navegador. Por exemplo, telefones Android baratos em países em desenvolvimento executam principalmente o Chrome e cortarão a mostarda, apesar de sua memória limitada e recursos de CPU.

    Eventualmente, usando o cabeçalho Device Memory Client Hints, poderemos direcionar dispositivos de baixo custo de forma mais confiável. No momento da escrita, o cabeçalho é suportado apenas em Blink (vale para dicas de clientes em geral). Como a memória do dispositivo também possui uma API JavaScript que está disponível no Chrome, uma opção pode ser detectar recursos com base na API e retornar ao padrão de módulo/nomódulo se não for suportado ( obrigado, Yoav! ).

  3. Você está usando trepidação de árvores, elevação de escopo e divisão de código?
    Tree-shake é uma maneira de limpar seu processo de compilação incluindo apenas o código que é realmente usado na produção e eliminando importações não utilizadas no Webpack. Com Webpack e Rollup, também temos escopo de elevação que permite que ambas as ferramentas detectem onde o encadeamento de import pode ser nivelado e convertido em uma função embutida sem comprometer o código. Com o Webpack, também podemos usar JSON Tree Shaking.

    A divisão de código é outro recurso do Webpack que divide sua base de código em "pedaços" que são carregados sob demanda. Nem todo o JavaScript precisa ser baixado, analisado e compilado imediatamente. Depois de definir os pontos de divisão em seu código, o Webpack pode cuidar das dependências e dos arquivos de saída. Ele permite que você mantenha o download inicial pequeno e solicite o código sob demanda quando solicitado pelo aplicativo. Alexander Kondrov tem uma introdução fantástica à divisão de código com Webpack e React.

    Considere o uso de preload-webpack-plugin que leva as rotas de divisão de código e, em seguida, solicita ao navegador para pré-carregá-las usando <link rel="preload"> ou <link rel="prefetch"> . As diretivas inline do Webpack também dão algum controle sobre o preload -carregamento/ prefetch . (Cuidado com os problemas de priorização.)

    Onde definir pontos de divisão? Ao rastrear quais partes de CSS/JavaScript são usadas e quais não são usadas. Umar Hansa explica como você pode usar o Code Coverage do Devtools para alcançá-lo.

    Ao lidar com aplicativos de página única, precisamos de algum tempo para inicializar o aplicativo antes de podermos renderizar a página. Sua configuração exigirá sua solução personalizada, mas você pode ficar atento aos módulos e técnicas para acelerar o tempo de renderização inicial. Por exemplo, veja como depurar o desempenho do React e eliminar problemas comuns de desempenho do React e veja como melhorar o desempenho no Angular. Em geral, a maioria dos problemas de desempenho vem do momento inicial para inicializar o aplicativo.

    Então, qual é a melhor maneira de dividir o código de forma agressiva, mas não muito agressiva? De acordo com Phil Walton, "além da divisão de código por meio de importações dinâmicas, [poderíamos] também usar a divisão de código no nível do pacote , onde cada módulo de nó importado é colocado em um pedaço com base no nome do pacote". Phil fornece um tutorial sobre como construí-lo também.

  4. Podemos melhorar a saída do Webpack?
    Como o Webpack é frequentemente considerado misterioso, existem muitos plugins do Webpack que podem ser úteis para reduzir ainda mais a saída do Webpack. Abaixo estão alguns dos mais obscuros que podem precisar de um pouco mais de atenção.

    Um dos interessantes vem do tópico de Ivan Akulov. Imagine que você tem uma função que chama uma vez, armazena seu resultado em uma variável e não usa essa variável. A agitação da árvore removerá a variável, mas não a função, porque pode ser usada de outra forma. No entanto, se a função não for usada em nenhum lugar, convém removê-la. Para fazer isso, anexe a chamada da função com /*#__PURE__*/ que é suportado pelo Uglify e Terser — pronto!

    Uma captura de tela do código JS em um editor mostrando como a função PURE pode ser usada
    Para remover tal função quando seu resultado não for usado, anexe a chamada de função com /*#__PURE__*/ . Via Ivan Akulov.(Grande visualização)

    Aqui estão algumas das outras ferramentas que Ivan recomenda:

    • purgecss-webpack-plugin remove classes não utilizadas, especialmente quando você está usando Bootstrap ou Tailwind.
    • Habilite optimization.splitChunks: 'all' com split-chunks-plugin. Isso faria com que o webpack dividisse automaticamente seus pacotes de entrada para um melhor armazenamento em cache.
    • Defina optimization.runtimeChunk: true . Isso moveria o tempo de execução do webpack para uma parte separada — e também melhoraria o armazenamento em cache.
    • google-fonts-webpack-plugin baixa arquivos de fonte, para que você possa servi-los de seu servidor.
    • workbox-webpack-plugin permite que você gere um service worker com uma configuração de pré-cache para todos os seus ativos de webpack. Além disso, verifique Service Worker Packages, um guia abrangente de módulos que podem ser aplicados imediatamente. Ou use preload-webpack-plugin para gerar preload -carregamento/ prefetch -busca para todos os fragmentos de JavaScript.
    • speed-measure-webpack-plugin mede a velocidade de compilação do seu webpack, fornecendo informações sobre quais etapas do processo de compilação são mais demoradas.
    • duplicado-pacote-verificador-webpack-plugin avisa quando seu pacote contém várias versões do mesmo pacote.
    • Use o isolamento de escopo e reduza os nomes das classes CSS dinamicamente no momento da compilação.

Uma captura de tela de um terminal mostrando como o carregador de webpack chamado responsivo-loader pode ser usado para ajudá-lo a gerar imagens responsivas prontas para uso
Acelerar suas imagens é servir imagens menores em telas menores. Com carregador responsivo. Via Ivan Akulov. (Visualização grande)
  1. Você pode descarregar JavaScript em um Web Worker?
    Para reduzir o impacto negativo no Tempo para Interação, pode ser uma boa ideia procurar descarregar JavaScript pesado em um Web Worker.

    À medida que a base de código continua crescendo, os gargalos de desempenho da interface do usuário aparecerão, diminuindo a experiência do usuário. Isso ocorre porque as operações do DOM estão sendo executadas junto com seu JavaScript no encadeamento principal. Com os web workers, podemos mover essas operações caras para um processo em segundo plano que está sendo executado em um thread diferente. Casos de uso típicos para web workers são a pré-busca de dados e Progressive Web Apps para carregar e armazenar alguns dados com antecedência para que você possa usá-los mais tarde, quando necessário. E você pode usar o Comlink para agilizar a comunicação entre a página principal e o trabalhador. Ainda há muito trabalho a fazer, mas estamos chegando lá.

    Existem alguns estudos de caso interessantes sobre web workers que mostram diferentes abordagens para mover a estrutura e a lógica do aplicativo para web workers. A conclusão: em geral, ainda existem alguns desafios, mas já existem alguns bons casos de uso ( obrigado, Ivan Akulov! ).

    A partir do Chrome 80, foi lançado um novo modo para trabalhadores da Web com benefícios de desempenho de módulos JavaScript, chamados de trabalhadores de módulo. Podemos alterar o carregamento e a execução do script para corresponder ao script type="module" , além disso, também podemos usar importações dinâmicas para código de carregamento lento sem bloquear a execução do trabalhador.

    Como começar? Aqui estão alguns recursos que valem a pena analisar:

    • A Surma publicou um excelente guia sobre como executar JavaScript no thread principal do navegador e também Quando você deve usar Web Workers?
    • Além disso, confira a palestra de Surma sobre a arquitetura de thread principal.
    • A Quest to Guaranteeness por Shubhie Panicker e Jason Miller fornecem uma visão detalhada de como usar os web workers e quando evitá-los.
    • Saindo do caminho dos usuários: menos problemas com Web Workers destaca padrões úteis para trabalhar com Web Workers, maneiras eficazes de se comunicar entre workers, lidar com processamento de dados complexos fora do thread principal e testá-los e depurá-los.
    • Workerize permite mover um módulo para um Web Worker, refletindo automaticamente as funções exportadas como proxies assíncronos.
    • Se você estiver usando o Webpack, poderá usar o workerize-loader. Alternativamente, você também pode usar o plugin do trabalhador.
    Código no DOM mostrado à esquerda como um exemplo do que usar e evitar ao usar web workers
    Use web workers quando o código bloquear por muito tempo, mas evite-os quando você confiar no DOM, lidar com a resposta de entrada e precisar de um atraso mínimo. (via Addy Osmani) (Visualização grande)

    Observe que os Web Workers não têm acesso ao DOM porque o DOM não é "thread-safe" e o código que eles executam precisa estar contido em um arquivo separado.

  2. Você pode descarregar "caminhos quentes" para o WebAssembly?
    Poderíamos descarregar tarefas computacionalmente pesadas para o WebAssembly ( WASM ), um formato de instrução binária, projetado como um destino portátil para compilação de linguagens de alto nível como C/C++/Rust. Seu suporte ao navegador é notável e recentemente se tornou viável, pois as chamadas de função entre JavaScript e WASM estão ficando mais rápidas. Além disso, ainda é compatível com a nuvem de borda da Fastly.

    É claro que o WebAssembly não deve substituir o JavaScript, mas pode complementá-lo nos casos em que você percebe problemas de CPU. Para a maioria dos aplicativos da Web, o JavaScript é mais adequado e o WebAssembly é melhor usado para aplicativos da Web computacionalmente intensivos , como jogos.

    Se você quiser saber mais sobre o WebAssembly:

    • Lin Clark escreveu uma série completa para WebAssembly e Milica Mihajlija fornece uma visão geral de como executar código nativo no navegador, por que você pode querer fazer isso e o que tudo isso significa para JavaScript e o futuro do desenvolvimento web.
    • Como usamos o WebAssembly para acelerar nosso aplicativo da Web em 20 vezes (estudo de caso) destaca um estudo de caso de como os cálculos lentos de JavaScript foram substituídos pelo WebAssembly compilado e trouxe melhorias significativas de desempenho.
    • Patrick Hamann tem falado sobre o crescente papel do WebAssembly e está desmistificando alguns mitos sobre o WebAssembly, explora seus desafios e podemos usá-lo praticamente em aplicativos hoje.
    • O Google Codelabs fornece uma introdução ao WebAssembly, um curso de 60 minutos no qual você aprenderá a pegar código nativo — em C e compilá-lo para o WebAssembly e depois chamá-lo diretamente do JavaScript.
    • Alex Danilo explicou o WebAssembly e como ele funciona em sua palestra no Google I/O. Além disso, Benedek Gagyi compartilhou um estudo de caso prático sobre WebAssembly, especificamente como a equipe o usa como formato de saída para sua base de código C++ para iOS, Android e site.

    Ainda não tem certeza sobre quando usar Web Workers, Web Assembly, streams ou talvez WebGL JavaScript API para acessar a GPU? Acelerar JavaScript é um guia curto, mas útil, que explica quando usar o quê e por quê - também com um fluxograma útil e muitos recursos úteis.

Uma ilustração de C++, C ou Rust mostrada à esquerda com uma seta mostrando para um navegador que inclui binários WASM adicionados ao JavaScript, CSS e HTML
Milica Mihajlija fornece uma visão geral de como o WebAssembly funciona e por que ele é útil. (Visualização grande)
  1. Servimos código legado apenas para navegadores legados?
    Com o ES2017 sendo incrivelmente bem suportado em navegadores modernos, podemos usar o babelEsmPlugin para apenas transpilar recursos do ES2017+ não suportados pelos navegadores modernos que você está direcionando.

    Houssein Djirdeh e Jason Miller publicaram recentemente um guia abrangente sobre como transpilar e servir JavaScript moderno e legado, detalhando como fazê-lo funcionar com Webpack e Rollup, e as ferramentas necessárias. Você também pode estimar quanto JavaScript pode eliminar em seu site ou pacotes de aplicativos.

    Os módulos JavaScript são suportados em todos os principais navegadores, então use script type="module" para permitir que navegadores com suporte ao módulo ES carreguem o arquivo, enquanto navegadores mais antigos podem carregar compilações legadas com script nomodule .

    Hoje em dia podemos escrever JavaScript baseado em módulo que roda nativamente no navegador, sem transpiladores ou empacotadores. O cabeçalho <link rel="modulepreload"> fornece uma maneira de iniciar o carregamento antecipado (e de alta prioridade) de scripts de módulo. Basicamente, é uma maneira bacana de ajudar a maximizar o uso da largura de banda, informando ao navegador sobre o que ele precisa buscar para que ele não fique preso a nada para fazer durante essas longas viagens de ida e volta. Além disso, Jake Archibald publicou um artigo detalhado com dicas e coisas para se ter em mente com os Módulos ES que vale a pena ler.

Scripts embutidos são adiados até que scripts externos bloqueados e scripts embutidos sejam executados
Jake Archibald publicou um artigo detalhado com dicas e coisas a serem lembradas com os Módulos ES, por exemplo, scripts inline são adiados até que scripts externos bloqueados e scripts inline sejam executados. (Visualização grande)
  1. Identifique e reescreva o código legado com desacoplamento incremental .
    Projetos de longa duração tendem a acumular poeira e códigos datados. Revisite suas dependências e avalie quanto tempo seria necessário para refatorar ou reescrever o código legado que está causando problemas ultimamente. Claro, é sempre um grande empreendimento, mas uma vez que você conhece o impacto do código legado, você pode começar com o desacoplamento incremental.

    Primeiro, configure métricas que rastreiem se a proporção de chamadas de código legado permanece constante ou diminui, não aumenta. Desencoraje publicamente a equipe de usar a biblioteca e certifique-se de que seu CI alerte os desenvolvedores se ela for usada em pull requests. polyfills podem ajudar na transição do código legado para a base de código reescrita que usa recursos padrão do navegador.

  2. Identifique e remova CSS/JS não utilizado .
    A cobertura de código CSS e JavaScript no Chrome permite que você saiba qual código foi executado/aplicado e qual não foi. Você pode começar a gravar a cobertura, executar ações em uma página e explorar os resultados da cobertura do código. Depois de detectar o código não utilizado, encontre esses módulos e carregue lentamente com import() (veja o tópico inteiro). Em seguida, repita o perfil de cobertura e confirme que agora está enviando menos código no carregamento inicial.

    Você pode usar o Puppeteer para coletar cobertura de código programaticamente. O Chrome também permite exportar resultados de cobertura de código. Como Andy Davies observou, você pode querer coletar cobertura de código para navegadores modernos e legados.

    Existem muitos outros casos de uso e ferramentas para o Puppetter que podem precisar de um pouco mais de exposição:

    • Casos de uso para Puppeteer, como, por exemplo, diferenciação visual automática ou monitoramento de CSS não utilizado a cada compilação,
    • Receitas de performance na web com o Puppeteer,
    • Ferramentas úteis para gravar e gerar scripts de Pupeeteer e Playwright,
    • Além disso, você pode até gravar testes diretamente no DevTools,
    • Visão geral abrangente do Puppeteer por Nitay Neeman, com exemplos e casos de uso.
    Uma captura de tela do Pupeteer Recorder à esquerda e uma captura de tela do Puppeteer Sandbox mostrada à direita
    Podemos usar o Puppeteer Recorder e o Puppeteer Sandbox para gravar a interação do navegador e gerar scripts do Puppeteer e do Playwright. (Visualização grande)

    Além disso, purgecss, UnCSS e Helium podem ajudá-lo a remover estilos não utilizados do CSS. E se você não tiver certeza se um pedaço de código suspeito é usado em algum lugar, você pode seguir o conselho de Harry Roberts: crie um GIF transparente de 1×1px para uma classe específica e solte-o em um diretório dead/ , por exemplo, /assets/img/dead/comments.gif .

    Depois disso, você define essa imagem específica como plano de fundo no seletor correspondente em seu CSS, senta e espera alguns meses se o arquivo aparecer em seus logs. Se não houver entradas, ninguém teve esse componente legado renderizado em sua tela: você provavelmente pode ir em frente e excluir tudo.

    Para o departamento I-meel-adventurous , você pode até automatizar a coleta de CSS não utilizado por meio de um conjunto de páginas monitorando o DevTools usando o DevTools.

Tabela de comparação do Webpack
Em seu artigo, Benedikt Rotsch mostrou que uma mudança de Moment.js para date-fns poderia reduzir cerca de 300ms para First paint em 3G e um celular de baixo custo. (Visualização grande)
  1. Reduza o tamanho de seus pacotes JavaScript.
    Como Addy Osmani observou, há uma grande chance de você estar enviando bibliotecas JavaScript completas quando você precisa apenas de uma fração, junto com polyfills datados para navegadores que não precisam deles, ou apenas código duplicado. Para evitar a sobrecarga, considere usar webpack-libs-optimizations que removem métodos não utilizados e polyfills durante o processo de compilação.

    Verifique e revise os polyfills que você está enviando para navegadores legados e para navegadores modernos e seja mais estratégico sobre eles. Dê uma olhada no polyfill.io, que é um serviço que aceita uma solicitação de um conjunto de recursos do navegador e retorna apenas os polyfills necessários ao navegador solicitante.

    Adicione também a auditoria de pacote ao seu fluxo de trabalho regular. Pode haver algumas alternativas leves para bibliotecas pesadas que você adicionou anos atrás, por exemplo, Moment.js (agora descontinuado) pode ser substituído por:

    • API de internacionalização nativa,
    • Day.js com uma API Moment.js e padrões familiares,
    • data-fns ou
    • Luxon.
    • Você também pode usar o Skypack Discover, que combina recomendações de pacotes revisadas por humanos com uma pesquisa focada na qualidade.

    A pesquisa de Benedikt Rotsch mostrou que uma mudança de Moment.js para date-fns poderia reduzir cerca de 300ms para a primeira pintura em 3G e um telefone celular de baixo custo.

    Para auditoria de pacote, o Bundlephobia pode ajudar a encontrar o custo de adicionar um pacote npm ao seu pacote. size-limit estende a verificação básica do tamanho do pacote com detalhes sobre o tempo de execução do JavaScript. Você pode até integrar esses custos com uma auditoria personalizada do Lighthouse. Isso vale para frameworks também. Ao remover ou aparar o adaptador Vue MDC (Material Components for Vue), os estilos caem de 194 KB para 10 KB.

    Existem muitas outras ferramentas para ajudá-lo a tomar uma decisão informada sobre o impacto de suas dependências e alternativas viáveis:

    • webpack-bundle-analyzer
    • Explorador do mapa de origem
    • Pacote amigo
    • Bundlefobia
    • A análise do Webpack mostra por que um módulo específico está incluído no pacote.
    • bundle-wizard também cria um mapa de dependências para a página inteira.
    • Plugin de tamanho do Webpack
    • Custo de importação para código visual

    Como alternativa ao envio de toda a estrutura, você pode aparar sua estrutura e compilá-la em um pacote JavaScript bruto que não requer código adicional. Svelte faz isso, assim como o plugin Rawact Babel que transpila componentes React.js para operações DOM nativas em tempo de compilação. Por quê? Bem, como os mantenedores explicam, "react-dom inclui código para cada componente/HTMLElement possível que pode ser renderizado, incluindo código para renderização incremental, agendamento, manipulação de eventos, etc. carregamento de página). Para esses aplicativos, pode fazer sentido usar operações DOM nativas para construir a interface de usuário interativa."

size-limit fornece verificação básica do tamanho do pacote com detalhes sobre o tempo de execução do JavaScript também
size-limit também fornece uma verificação básica do tamanho do pacote com detalhes sobre o tempo de execução do JavaScript. (Visualização grande)
  1. Usamos hidratação parcial?
    Com a quantidade de JavaScript usada nos aplicativos, precisamos descobrir maneiras de enviar o mínimo possível para o cliente. Uma maneira de fazer isso – e já abordamos brevemente – é com hidratação parcial. A ideia é bem simples: em vez de fazer SSR e depois enviar o aplicativo inteiro para o cliente, apenas pequenas partes do JavaScript do aplicativo seriam enviadas ao cliente e depois hidratadas. Podemos pensar nisso como vários pequenos aplicativos React com várias raízes de renderização em um site estático.

    No artigo "O caso da hidratação parcial (com Next e Preact)", Lukas Bombach explica como a equipe por trás do Welt.de, um dos veículos de notícias da Alemanha, obteve melhor desempenho com hidratação parcial. Você também pode verificar o repositório GitHub do próximo super desempenho com explicações e trechos de código.

    Você também pode considerar opções alternativas:

    • hidratação parcial com Preact e Eleventy,
    • hidratação progressiva no repositório React GitHub,
    • hidratação preguiçosa no Vue.js (repositório do GitHub),
    • Importar no padrão de interação para carregar lentamente recursos não críticos (por exemplo, componentes, incorporações) quando um usuário interage com a interface do usuário que precisa.

    Jason Miller publicou demos de trabalho sobre como a hidratação progressiva pode ser implementada com o React, para que você possa usá-los imediatamente: demo 1, demo 2, demo 3 (também disponível no GitHub). Além disso, você pode olhar para a biblioteca de componentes react-prerendered.

    +485 KB de JavaScript no loadshare() no Google Docs
    A importação na interação para código primário só deve ser feita se você não conseguir pré-buscar recursos antes da interação. (Visualização grande)
  2. Otimizamos a estratégia para React/SPA?
    Lutando com o desempenho em seu aplicativo de página única? Jeremy Wagner explorou o impacto do desempenho da estrutura do lado do cliente em uma variedade de dispositivos, destacando algumas das implicações e diretrizes das quais devemos estar cientes ao usar um.

    Como resultado, aqui está uma estratégia de SPA que Jeremy sugere usar para o framework React (mas não deve mudar significativamente para outros frameworks):

    • Refatore componentes com estado como componentes sem estado sempre que possível.
    • Pré-renderize componentes sem estado quando possível para minimizar o tempo de resposta do servidor. Renderize apenas no servidor.
    • Para componentes com estado com interatividade simples, considere a pré-renderização ou renderização do servidor desse componente e substitua sua interatividade por ouvintes de eventos independentes de estrutura .
    • Se você precisar hidratar componentes com estado no cliente, use hidratação lenta na visibilidade ou na interação.
    • Para componentes com hidratação preguiçosa, agende sua hidratação durante o tempo ocioso do encadeamento principal com requestIdleCallback .

    Existem algumas outras estratégias que você pode querer seguir ou revisar:

    • Considerações de desempenho para CSS-in-JS em aplicativos React
    • Reduza o tamanho do pacote Next.js carregando polyfills somente quando necessário, usando importações dinâmicas e hidratação lenta.
    • Segredos do JavaScript: Um conto de React, Performance Optimization e Multi-threading, uma longa série de 7 partes sobre como melhorar os desafios da interface do usuário com React,
    • Como medir o desempenho do React e como criar o perfil de aplicativos React.
    • Criando animações da web para dispositivos móveis em React, uma palestra fantástica de Alex Holachek, juntamente com slides e repositório do GitHub ( obrigado pela dica, Addy! ).
    • webpack-libs-optimizations é um repositório GitHub fantástico com muitas otimizações úteis relacionadas ao desempenho específicas do Webpack. Mantido por Ivan Akulov.
    • Melhorias de desempenho do React no Notion, um guia de Ivan Akulov sobre como melhorar o desempenho no React, com muitas dicas úteis para tornar o aplicativo cerca de 30% mais rápido.
    • React Refresh Webpack Plugin (experimental) permite o recarregamento a quente que preserva o estado do componente e suporta ganchos e componentes de função.
    • Cuidado com os componentes do servidor React Server de tamanho zero, um novo tipo de componente proposto que não terá impacto no tamanho do pacote. O projeto está atualmente em desenvolvimento, mas qualquer feedback da comunidade é muito apreciado (ótimo explicador por Sophie Alpert).
  3. Você está usando a pré-busca preditiva para fragmentos de JavaScript?
    Poderíamos usar heurísticas para decidir quando pré-carregar partes do JavaScript. Guess.js é um conjunto de ferramentas e bibliotecas que usam dados do Google Analytics para determinar qual página um usuário provavelmente visitará em seguida em uma determinada página. Com base nos padrões de navegação do usuário coletados do Google Analytics ou de outras fontes, o Guess.js cria um modelo de aprendizado de máquina para prever e pré-buscar o JavaScript que será necessário em cada página subsequente.

    Portanto, cada elemento interativo está recebendo uma pontuação de probabilidade de engajamento e, com base nessa pontuação, um script do lado do cliente decide pré-buscar um recurso antecipadamente. Você pode integrar a técnica ao seu aplicativo Next.js, Angular e React, e há um plugin Webpack que automatiza o processo de configuração também.

    Obviamente, você pode estar solicitando ao navegador que consuma dados desnecessários e pré-busque páginas indesejáveis, portanto, é uma boa ideia ser bastante conservador no número de solicitações pré-buscadas. Um bom caso de uso seria a pré-busca de scripts de validação necessários no check-out ou a pré-busca especulativa quando uma call-to-action crítica chega à janela de visualização.

    Precisa de algo menos sofisticado? DNStradamus faz a pré-busca de DNS para links de saída conforme eles aparecem na janela de visualização. Quicklink, InstantClick e Instant.page são pequenas bibliotecas que pré-buscam automaticamente links na janela de visualização durante o tempo ocioso na tentativa de fazer as navegações da próxima página carregarem mais rapidamente. Quicklink permite pré-buscar rotas do React Router e Javascript; além de considerar os dados, não faz pré-busca em 2G ou se Data-Saver estiver ativado. O mesmo acontece com o Instant.page se o modo estiver definido para usar a pré- busca da janela de visualização (que é um padrão).

    Se você quiser examinar a ciência da pré-busca preditiva em todos os detalhes, Divya Tagtachian tem uma ótima palestra sobre A arte da pré-busca preditiva, cobrindo todas as opções do início ao fim.

  4. Aproveite as otimizações para seu mecanismo JavaScript de destino.
    Estude o que os mecanismos JavaScript dominam em sua base de usuários e explore maneiras de otimizar para eles. Por exemplo, ao otimizar para V8, que é usado em navegadores Blink, tempo de execução Node.js e Electron, use o streaming de script para scripts monolíticos.

    O streaming de script permite que scripts async ou defer scripts sejam analisados ​​em um thread de segundo plano separado assim que o download começa, portanto, em alguns casos, melhorando os tempos de carregamento da página em até 10%. Practically, use <script defer> in the <head> , so that the browsers can discover the resource early and then parse it on the background thread.

    Caveat : Opera Mini doesn't support script deferment, so if you are developing for India or Africa, defer will be ignored, resulting in blocking rendering until the script has been evaluated (thanks Jeremy!) .

    You could also hook into V8's code caching as well, by splitting out libraries from code using them, or the other way around, merge libraries and their uses into a single script, group small files together and avoid inline scripts. Or perhaps even use v8-compile-cache.

    When it comes to JavaScript in general, there are also some practices that are worth keeping in mind:

    • Clean Code concepts for JavaScript, a large collection of patterns for writing readable, reusable, and refactorable code.
    • You can Compress data from JavaScript with the CompressionStream API, eg to gzip before uploading data (Chrome 80+).
    • Detached window memory leaks and Fixing memory leaks in web apps are detailed guides on how to find and fix tricky JavaScript memory leaks. Plus, you can use queryObjects(SomeConstructor) from the DevTools Console ( thanks, Mathias! ).
    • Reexports are bad for loading and runtime performance, and avoiding them can help reduce the bundle size significantly.
    • We can improve scroll performance with passive event listeners by setting a flag in the options parameter. So browsers can scroll the page immediately, rather than after the listener has finished. (via Kayce Basques).
    • If you have any scroll or touch* listeners, pass passive: true to addEventListener. This tells the browser you're not planning to call event.preventDefault() inside, so it can optimize the way it handles these events. (via Ivan Akulov)
    • We can achieve better JavaScript scheduling with isInputPending(), a new API that attempts to bridge the gap between loading and responsiveness with the concepts of interrupts for user inputs on the web, and allows for JavaScript to be able to check for input without yielding to the browser.
    • You can also automatically remove an event listener after it has executed.
    • Firefox's recently released Warp, a significant update to SpiderMonkey (shipped in Firefox 83), Baseline Interpreter and there are a few JIT Optimization Strategies available as well.
An illustration to help you understand time loading and responsiveness
A blue banner showing running JS with white lines in regular gaps representing the time when we proactively check whether there’s user input without incurring the overhead of yielding execution to the browser and back
isInputPending() is a new browser API that attempts to bridge the gap between loading and responsiveness.(Large preview)
An illustration of a map showing the chain of each request to different domains, all the way to eighth-party scripts
The request map for CNN.com showing the chain of each request to different domains, all the way to eighth-party scripts. Fonte. (Visualização grande)
  1. Always prefer to self-host third-party assets.
    Yet again, self-host your static assets by default. It's common to assume that if many sites use the same public CDN and the same version of a JavaScript library or a web font, then the visitors would land on our site with the scripts and fonts already cached in their browser, speeding up their experience considerably. However, it's very unlikely to happen.

    For security reasons, to avoid fingerprinting, browsers have been implementing partitioned caching that was introduced in Safari back in 2013, and in Chrome last year. So if two sites point to the exact same third-party resource URL, the code is downloaded once per domain , and the cache is "sandboxed" to that domain due to privacy implications ( thanks, David Calhoun! ). Hence, using a public CDN will not automatically lead to better performance.

    Furthermore, it's worth noting that resources don't live in the browser's cache as long as we might expect, and first-party assets are more likely to stay in the cache than third-party assets. Therefore, self-hosting is usually more reliable and secure, and better for performance, too.

  2. Constrain the impact of third-party scripts.
    With all performance optimizations in place, often we can't control third-party scripts coming from business requirements. Third-party-scripts metrics aren't influenced by end-user experience, so too often one single script ends up calling a long tail of obnoxious third-party scripts, hence ruining a dedicated performance effort. To contain and mitigate performance penalties that these scripts bring along, it's not enough to just defer their loading and execution and warm up connections via resource hints, ie dns-prefetch or preconnect .

    Currently 57% of all JavaScript code excution time is spent on third-party code. The median mobile site accesses 12 third-party domains , with a median of 37 different requests (or about 3 requests made to each third party).

    Furthermore, these third-parties often invite fourth-party scripts to join in, ending up with a huge performance bottleneck, sometimes going as far as to the eigth-party scripts on a page. So regularly auditing your dependencies and tag managers can bring along costly surprises.

    Another problem, as Yoav Weiss explained in his talk on third-party scripts, is that in many cases these scripts download resources that are dynamic. The resources change between page loads, so we don't necessarily know which hosts the resources will be downloaded from and what resources they would be.

    Deferring, as shown above, might be just a start though as third-party scripts also steal bandwidth and CPU time from your app. We could be a bit more aggressive and load them only when our app has initialized.

    /* Before */ const App = () => { return <div> <script> window.dataLayer = window.dataLayer || []; function gtag(){...} gtg('js', new Date()); </script> </div> } /* After */ const App = () => { const[isRendered, setRendered] = useState(false); useEffect(() => setRendered(true)); return <div> {isRendered ? <script> window.dataLayer = window.dataLayer || []; function gtag(){...} gtg('js', new Date()); </script> : null} </div> }

    In a fantastic post on "Reducing the Site-Speed Impact of Third-Party Tags", Andy Davies explores a strategy of minimizing the footprint of third-parties — from identifying their costs towards reducing their impact.

    According to Andy, there are two ways tags impact site-speed — they compete for network bandwidth and processing time on visitors' devices, and depending on how they're implemented, they can delay HTML parsing as well. So the first step is to identify the impact that third-parties have, by testing the site with and without scripts using WebPageTest. With Simon Hearne's Request Map, we can also visualize third-parties on a page along with details on their size, type and what triggered their load.

    Preferably self-host and use a single hostname, but also use a request map to exposes fourth-party calls and detect when the scripts change. You can use Harry Roberts' approach for auditing third parties and produce spreadsheets like this one (also check Harry's auditing workflow).

    Afterwards, we can explore lightweight alternatives to existing scripts and slowly replace duplicates and main culprits with lighter options. Perhaps some of the scripts could be replaced with their fallback tracking pixel instead of the full tag.

    Left example showing 3KB of JavaScript using the lite-youtube custom element, middle and right example showing +540KB of JavaScript with the lite-youtube custom element
    Loading YouTube with facades, eg lite-youtube-embed that's significantly smaller than an actual YouTube player. (Fonte da imagem) (Visualização grande)

    If it's not viable, we can at least lazy load third-party resources with facades, ie a static element which looks similar to the actual embedded third-party, but is not functional and therefore much less taxing on the page load. The trick, then, is to load the actual embed only on interaction .

    For example, we can use:

    • lite-vimeo-embed for the Vimeo player,
    • lite-vimeo for the Vimeo player,
    • lite-youtube-embed for the YouTube player,
    • react-live-chat-loader for a live chat (case study, and another case-study),
    • lazyframe for iframes.

    One of the reasons why tag managers are usually large in size is because of the many simultaneous experiments that are running at the same time, along with many user segments, page URLs, sites etc., so according to Andy, reducing them can reduce both the download size and the time it takes to execute the script in the browser.

    And then there are anti-flicker snippets. Third-parties such as Google Optimize, Visual Web Optimizer (VWO) and others are unanimous in using them. These snippets are usually injected along with running A/B tests : to avoid flickering between the different test scenarios, they hide the body of the document with opacity: 0 , then adds a function that gets called after a few seconds to bring the opacity back. This often results in massive delays in rendering due to massive client-side execution costs.

    Seven previews shown from 0.0 seconds to 6.0 seconds showing how and when contents are hidden by the anti-flicker snippet when a visitor initiates navigation
    With A/B testing in use, customers would often see flickering like this one. Anti-Flicker snippets prevent that, but they also cost in performance. Via Andy Davies. (Visualização grande)

    Therefore keep track how often the anti-flicker timeout is triggered and reduce the timeout. Default blocks display of your page by up to 4s which will ruin conversion rates. According to Tim Kadlec, "Friends don't let friends do client side A/B testing". Server-side A/B testing on CDNs (eg Edge Computing, or Edge Slice Rerendering) is always a more performant option.

    If you have to deal with almighty Google Tag Manager , Barry Pollard provides some guidelines to contain the impact of Google Tag Manager. Also, Christian Schaefer explores strategies for loading ads.

    Cuidado: alguns widgets de terceiros se escondem das ferramentas de auditoria, então podem ser mais difíceis de detectar e medir. Para testar terceiros, examine os resumos de baixo para cima na página Perfil de desempenho no DevTools, teste o que acontece se uma solicitação for bloqueada ou tiver expirado - para o último, você pode usar o servidor Blackhole do WebPageTest blackhole.webpagetest.org que você pode apontar domínios específicos para o seu arquivo hosts .

    Que opções temos então? Considere o uso de service workers executando o download do recurso com um tempo limite e, se o recurso não tiver respondido dentro de um determinado tempo limite, retorne uma resposta vazia para informar ao navegador para continuar com a análise da página. Você também pode registrar ou bloquear solicitações de terceiros que não são bem-sucedidas ou que não atendem a determinados critérios. Se você puder, carregue o script de terceiros do seu próprio servidor em vez do servidor do fornecedor e carregue-o lentamente.

    Outra opção é estabelecer uma Política de Segurança de Conteúdo (CSP) para restringir o impacto de scripts de terceiros, por exemplo, não permitir o download de áudio ou vídeo. A melhor opção é incorporar scripts via <iframe> para que os scripts sejam executados no contexto do iframe e, portanto, não tenham acesso ao DOM da página e não possam executar código arbitrário em seu domínio. Iframes podem ser ainda mais restritos usando o atributo sandbox , para que você possa desabilitar qualquer funcionalidade que o iframe possa fazer, por exemplo, impedir a execução de scripts, evitar alertas, envio de formulários, plugins, acesso à navegação superior e assim por diante.

    Você também pode manter terceiros sob controle por meio de linting de desempenho no navegador com políticas de recursos, um recurso relativamente novo que permite ativar ou desativar determinados recursos do navegador em seu site. (Como nota lateral, também pode ser usado para evitar imagens superdimensionadas e não otimizadas, mídia não dimensionada, scripts de sincronização e outros). Atualmente suportado em navegadores baseados em Blink.

    /* Via Tim Kadlec. https://timkadlec.com/remembers/2020-02-20-in-browser-performance-linting-with-feature-policies/ */ /* Block the use of the Geolocation API with a Feature-Policy header. */ Feature-Policy: geolocation 'none'
    /* Via Tim Kadlec. https://timkadlec.com/remembers/2020-02-20-in-browser-performance-linting-with-feature-policies/ */ /* Block the use of the Geolocation API with a Feature-Policy header. */ Feature-Policy: geolocation 'none'

    Como muitos scripts de terceiros estão sendo executados em iframes, você provavelmente precisa restringir suas permissões. Os iframes em sandbox são sempre uma boa ideia, e cada uma das limitações pode ser eliminada por meio de vários valores de allow no atributo sandbox . O sandboxing é suportado em quase todos os lugares, portanto, restrinja os scripts de terceiros ao mínimo do que eles devem ter permissão para fazer.

    Uma captura de tela do site ThirdPartyWeb.Today visualizando quanto tempo os scripts da entidade levam para serem executados em média
    ThirdPartyWeb.Today agrupa todos os scripts de terceiros por categoria (analítica, social, publicidade, hospedagem, gerenciador de tags etc.) e visualiza quanto tempo os scripts da entidade levam para serem executados (em média). (Visualização grande)

    Considere usar um Observador de Interseção; que permitiria que os anúncios fossem iframes enquanto ainda enviam eventos ou obtêm as informações de que precisam do DOM (por exemplo, visibilidade do anúncio). Fique atento a novas políticas, como política de recursos, limites de tamanho de recursos e prioridade de CPU/largura de banda para limitar recursos e scripts da Web prejudiciais que tornariam o navegador mais lento, por exemplo, scripts síncronos, solicitações XHR síncronas, document.write e implementações desatualizadas.

    Por fim, ao escolher um serviço de terceiros, considere verificar o ThirdPartyWeb.Today de Patrick Hulce, um serviço que agrupa todos os scripts de terceiros por categoria (analítica, social, publicidade, hospedagem, gerenciador de tags etc.) e visualiza quanto tempo os scripts da entidade levar para executar (em média). Obviamente, as maiores entidades têm o pior impacto no desempenho das páginas em que estão. Apenas percorrendo a página, você terá uma ideia da pegada de desempenho que deve esperar.

    Ah, e não se esqueça dos suspeitos de sempre: em vez de widgets de terceiros para compartilhamento, podemos usar botões estáticos de compartilhamento social (como por SSBG) e links estáticos para mapas interativos em vez de mapas interativos.

Um exemplo de gráfico comparando a porcentagem de solicitações de primeira e de terceiros: 399 KB no valor de 27% das solicitações da primeira parte e 1,15 MB no valor de 73% das solicitações de terceiros
A Casper.com publicou um estudo de caso detalhado sobre como eles conseguiram cortar 1,7 segundo do site hospedando o Optimizely. Pode valer a pena. (Fonte da imagem) (Visualização grande)
  1. Defina os cabeçalhos de cache HTTP corretamente.
    O armazenamento em cache parece ser uma coisa tão óbvia a fazer, mas pode ser muito difícil acertar. Precisamos verificar novamente se expires , max-age , cache-control e outros cabeçalhos de cache HTTP foram definidos corretamente. Sem os cabeçalhos de cache HTTP adequados, os navegadores os definirão automaticamente em 10% do tempo decorrido desde last-modified , terminando com um possível armazenamento em cache insuficiente e excessivo.

    Em geral, os recursos devem ser armazenados em cache por um período muito curto (se for provável que mudem) ou indefinidamente (se forem estáticos) — você pode apenas alterar sua versão na URL quando necessário. Você pode chamar isso de estratégia Cache-Forever, na qual podemos retransmitir os cabeçalhos Cache-Control e Expires para o navegador para permitir que os ativos expirem apenas em um ano. Portanto, o navegador nem faria uma solicitação para o ativo se o tivesse no cache.

    A exceção são as respostas da API (por exemplo, /api/user ). Para evitar o armazenamento em cache, podemos usar private, no store e not max-age=0, no-store :

     Cache-Control: private, no-store

    Use Cache-control: immutable para evitar a revalidação de tempos de vida de cache explícitos longos quando os usuários pressionam o botão de recarregar. Para o caso de recarga, immutable salva solicitações HTTP e melhora o tempo de carregamento do HTML dinâmico, pois eles não competem mais com a multiplicidade de respostas 304.

    Um exemplo típico em que queremos usar immutable ​​são ativos CSS/JavaScript com um hash em seu nome. Para eles, provavelmente queremos armazenar em cache o maior tempo possível e garantir que nunca sejam revalidados:

    Cache-Control: max-age: 31556952, immutable

    De acordo com a pesquisa de Colin Bendell, immutable reduz os redirecionamentos 304 em cerca de 50%, pois mesmo com max-age em uso, os clientes ainda são revalidados e bloqueados na atualização. É suportado no Firefox, Edge e Safari e o Chrome ainda está debatendo o problema.

    De acordo com o Web Almanac, "seu uso cresceu para 3,5% e é amplamente utilizado nas respostas de terceiros do Facebook e do Google".

    Eficácia do controle de cache em todos os continentes com dados recuperados do Android Chrome e iOS Safari
    Cache-Control: Immutable reduz 304s em cerca de 50%, de acordo com a pesquisa de Colin Bendell na Cloudinary. (Visualização grande)

    Você se lembra do bom e velho obsoleto-enquanto-revalida? Quando especificamos o tempo de armazenamento em cache com o cabeçalho de resposta Cache-Control (por exemplo Cache-Control: max-age=604800 ), após max-age expirar, o navegador irá buscar novamente o conteúdo solicitado, fazendo com que a página carregue mais lentamente. Essa desaceleração pode ser evitada com stale-while-revalidate ; ele basicamente define uma janela de tempo extra durante a qual um cache pode usar um ativo obsoleto, desde que o revalida assíncrono em segundo plano. Assim, ele "esconde" a latência (tanto na rede quanto no servidor) dos clientes.

    Em junho-julho de 2019, o Chrome e o Firefox lançaram o suporte de stale-while-revalidate no cabeçalho HTTP Cache-Control, portanto, ele deve melhorar as latências de carregamento de página subsequentes, pois os ativos obsoletos não estão mais no caminho crítico. Resultado: zero RTT para visualizações repetidas.

    Tenha cuidado com o cabeçalho de variação, especialmente em relação a CDNs, e atente para as Variantes de Representação HTTP que ajudam a evitar uma viagem de ida e volta adicional para validação sempre que uma nova solicitação difere ligeiramente (mas não significativamente) das solicitações anteriores ( obrigado, Guy e Mark ! ).

    Além disso, verifique novamente se você não está enviando cabeçalhos desnecessários (por exemplo x-powered-by , pragma , x-ua-compatible , expires , X-XSS-Protection e outros) e se inclui cabeçalhos úteis de segurança e desempenho (como como Content-Security-Policy , X-Content-Type-Options e outros). Por fim, lembre-se do custo de desempenho das solicitações CORS em aplicativos de página única.

    Observação : geralmente assumimos que os ativos armazenados em cache são recuperados instantaneamente, mas pesquisas mostram que recuperar um objeto do cache pode levar centenas de milissegundos. Na verdade, de acordo com Simon Hearne, "às vezes a rede pode ser mais rápida que o cache, e recuperar ativos do cache pode ser caro com um grande número de ativos em cache (não o tamanho do arquivo) e os dispositivos do usuário. Por exemplo: recuperação média de cache do Chrome OS dobra de ~50ms com 5 recursos em cache até ~100ms com 25 recursos".

    Além disso, geralmente assumimos que o tamanho do pacote não é um grande problema e os usuários farão o download uma vez e depois usarão a versão em cache. Ao mesmo tempo, com CI/CD, enviamos o código para produção várias vezes ao dia, o cache é invalidado todas as vezes, portanto, ser estratégico em relação ao armazenamento em cache.

    Quando se trata de cache, há muitos recursos que valem a pena ler:

    • Cache-Control for Civilians, um mergulho profundo em tudo que é armazenado em cache com Harry Roberts.
    • Cartilha do Heroku sobre cabeçalhos de cache HTTP,
    • Práticas recomendadas de armazenamento em cache por Jake Archibald,
    • Cartilha de cache HTTP por Ilya Grigorik,
    • Mantendo as coisas frescas com stale-while-revalidate por Jeff Posnick.
    • CS Visualized: CORS por Lydia Hallie é um ótimo explicador sobre CORS, como ele funciona e como entendê-lo.
    • Falando sobre CORS, aqui está uma pequena atualização sobre a Política de mesma origem de Eric Portis.
Um gráfico que mostra o tempo de recuperação do cache por contagem de ativos armazenados em cache com diferentes sistemas operacionais e navegadores nomeados à direita (de cima para baixo): Desktop Chrome OS, Tablet Android OS, Mobile Android OS, Desktop Mac =S X, Desktop Windows, Desktop Linux
Assumimos que os caches do navegador são quase instantâneos, mas os dados mostram que recuperar um objeto do cache pode levar centenas de milissegundos! Da pesquisa de Simon Hearne sobre Quando a rede é mais rápida que o cache. (Visualização grande)

Otimizações de entrega

  1. defer para carregar JavaScript crítico de forma assíncrona?
    Quando o usuário solicita uma página, o navegador busca o HTML e constrói o DOM, então busca o CSS e constrói o CSSOM, e então gera uma árvore de renderização combinando o DOM e o CSSOM. Se algum JavaScript precisar ser resolvido, o navegador não começará a renderizar a página até que ela seja resolvida, atrasando assim a renderização. Como desenvolvedores, temos que dizer explicitamente ao navegador para não esperar e começar a renderizar a página. A maneira de fazer isso para scripts é com os atributos defer e async em HTML.

    Na prática, é melhor usar defer em vez de async . Ah, qual é a diferença de novo? De acordo com Steve Souders, assim que os scripts async chegam, eles são executados imediatamente – assim que o script estiver pronto. Se isso acontecer muito rápido, por exemplo, quando o script já estiver no cache, ele pode bloquear o analisador HTML. Com defer , o navegador não executa scripts até que o HTML seja analisado. Portanto, a menos que você precise que o JavaScript seja executado antes de iniciar a renderização, é melhor usar defer . Além disso, vários arquivos assíncronos serão executados em uma ordem não determinística.

    Vale a pena notar que existem alguns equívocos sobre async e defer . Mais importante, async não significa que o código será executado sempre que o script estiver pronto; significa que ele será executado sempre que os scripts estiverem prontos e todo o trabalho de sincronização anterior estiver concluído. Nas palavras de Harry Roberts, "Se você colocar um script async após os scripts de sincronização, seu script async será tão rápido quanto o script de sincronização mais lento".

    Além disso, não é recomendado usar async e defer . Os navegadores modernos suportam ambos, mas sempre que ambos os atributos são usados, o async sempre vence.

    Se você quiser se aprofundar em mais detalhes, Milica Mihajlija escreveu um guia muito detalhado sobre como construir o DOM mais rapidamente, entrando nos detalhes da análise especulativa, assíncrona e adiada.

  2. Carregue lentamente componentes caros com IntersectionObserver e dicas de prioridade.
    Em geral, é recomendável carregar lentamente todos os componentes caros, como JavaScript pesado, vídeos, iframes, widgets e potencialmente imagens. O carregamento lento nativo já está disponível para imagens e iframes com o atributo de loading (somente Chromium). Sob o capô, esse atributo adia o carregamento do recurso até atingir uma distância calculada da janela de visualização.
    <!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />

    Esse limite depende de algumas coisas, desde o tipo de recurso de imagem que está sendo buscado até o tipo de conexão efetiva. Mas experimentos realizados usando o Chrome no Android sugerem que em 4G, 97,5% das imagens abaixo da dobra que são carregadas com preguiça foram totalmente carregadas em 10 ms após se tornarem visíveis, por isso deve ser seguro.

    Também podemos usar o atributo de importance ( high ou low ) em um elemento <script> , <img> ou <link> (somente Blink). Na verdade, é uma ótima maneira de priorizar imagens em carrosséis, bem como redefinir a prioridade de scripts. No entanto, às vezes podemos precisar de um controle um pouco mais granular.

    <!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />

    A maneira mais eficiente de fazer um carregamento lento um pouco mais sofisticado é usando a API Intersection Observer, que fornece uma maneira de observar as alterações de forma assíncrona na interseção de um elemento de destino com um elemento ancestral ou com a janela de visualização de um documento de nível superior. Basicamente, você precisa criar um novo objeto IntersectionObserver , que recebe uma função de retorno de chamada e um conjunto de opções. Em seguida, adicionamos um alvo para observar.

    A função de retorno de chamada é executada quando o destino se torna visível ou invisível, portanto, quando ela intercepta a viewport, você pode começar a executar algumas ações antes que o elemento se torne visível. Na verdade, temos um controle granular sobre quando o retorno de chamada do observador deve ser invocado, com rootMargin (margem ao redor da raiz) e threshold (um único número ou uma matriz de números que indicam em qual porcentagem da visibilidade do alvo estamos mirando).

    Alejandro Garcia Anglada publicou um tutorial útil sobre como realmente implementá-lo, Rahul Nanwani escreveu um post detalhado sobre carregamento lento de imagens de primeiro plano e plano de fundo, e o Google Fundamentals fornece um tutorial detalhado sobre carregamento lento de imagens e vídeos com o Intersection Observer também.

    Lembra-se de longas leituras de histórias com direção de arte com objetos em movimento e pegajosos? Você também pode implementar o scrollytelling de alto desempenho com o Intersection Observer.

    Verifique novamente o que mais você poderia carregar com preguiça. Até mesmo strings de tradução e emojis com carregamento lento podem ajudar. Ao fazer isso, o Mobile Twitter conseguiu alcançar uma execução JavaScript 80% mais rápida a partir do novo pipeline de internacionalização.

    Uma palavra rápida de cautela, porém: vale a pena notar que o carregamento lento deve ser uma exceção e não a regra. Provavelmente não é razoável carregar com preguiça qualquer coisa que você realmente queira que as pessoas vejam rapidamente, por exemplo, imagens de páginas de produtos, imagens de heróis ou um script necessário para que a navegação principal se torne interativa.

Um exemplo mostrando um limite antigo de 3000px com downloads de 160KB (à esquerda), enquanto o novo limite tem uma quantidade de 1250px com apenas 90KB de downloads (à direita), mostrando uma melhoria na economia de dados lentos de carregamento de img
Em conexões rápidas (por exemplo, 4G), os limites de distância da janela de visualização do Chrome foram recentemente reduzidos de 3000px para 1250px, e em conexões mais lentas (por exemplo, 3G), o limite mudou de 4000px para 2500px. (Visualização grande)
Uma ilustração com texto em torno de um telefone celular com a IU do Twitter exibida, explicando as melhorias de ferramentas de strings de tradução de carregamento lento
Com o carregamento lento de strings de tradução, o Mobile Twitter conseguiu uma execução JavaScript 80% mais rápida a partir do novo pipeline de internacionalização. (Crédito da imagem: Addy Osmani) (Visualização grande)
  1. Carregue imagens progressivamente.
    Você pode até levar o carregamento lento para o próximo nível adicionando carregamento progressivo de imagens às suas páginas. Da mesma forma que no Facebook, Pinterest, Medium e Wolt, você pode carregar imagens de baixa qualidade ou até desfocadas primeiro e, à medida que a página continuar a carregar, substitua-as pelas versões de qualidade total usando a técnica BlurHash ou LQIP (Low Quality Image Placeholders) técnica.

    As opiniões diferem se essas técnicas melhoram ou não a experiência do usuário, mas definitivamente melhora o tempo para a primeira pintura de conteúdo. Podemos até automatizá-lo usando SQIP que cria uma versão de baixa qualidade de uma imagem como um espaço reservado SVG, ou Espaços reservados de imagem de gradiente com gradientes lineares CSS.

    Esses espaços reservados podem ser incorporados ao HTML, pois são naturalmente compactados com métodos de compactação de texto. Em seu artigo, Dean Hume descreveu como essa técnica pode ser implementada usando o Intersection Observer.

    Cair pra trás? Se o navegador não suportar observador de interseção, ainda podemos carregar um polyfill com preguiça ou carregar as imagens imediatamente. E tem até uma biblioteca para isso.

    Quer ficar mais chique? Você pode rastrear suas imagens e usar formas e arestas primitivas para criar um espaço reservado SVG leve, carregá-lo primeiro e, em seguida, fazer a transição da imagem vetorial do espaço reservado para a imagem bitmap (carregada).

  2. Três versões diferentes mostrando a técnica de carregamento lento SVG de Jose M. Perez, uma versão semelhante à arte do cubismo à esquerda, uma versão desfocada pixelizada no meio e uma foto apropriada do próprio José à direita
    Técnica de carregamento lento SVG por Jose M. Perez. (Visualização grande)
  3. Você adia a renderização com content-visibility ?
    Para layouts complexos com muitos blocos de conteúdo, imagens e vídeos, decodificar dados e renderizar pixels pode ser uma operação bastante cara — especialmente em dispositivos de baixo custo. Com content-visibility: auto , podemos solicitar que o navegador ignore o layout dos filhos enquanto o contêiner estiver fora da janela de visualização.

    Por exemplo, você pode pular a renderização do rodapé e das seções finais no carregamento inicial:

    footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }

    Observe que a visibilidade do conteúdo: auto; se comporta como estouro: oculto; , mas você pode corrigi-lo aplicando padding-left e padding-right em vez do margin-left: auto; , margin-right: auto; e uma largura declarada. O preenchimento basicamente permite que os elementos transbordem a caixa de conteúdo e entrem na caixa de preenchimento sem deixar o modelo de caixa como um todo e ser cortado.

    Além disso, lembre-se de que você pode introduzir algum CLS quando o novo conteúdo for renderizado, portanto, é uma boa ideia usar contain-intrinsic-size com um espaço reservado de tamanho adequado ( obrigado, Una! ).

    Thijs Terluin tem muito mais detalhes sobre ambas as propriedades e como contain-intrinsic-size é calculado pelo navegador, Malte Ubl mostra como você pode calculá-lo e um breve vídeo explicativo de Jake e Surma explica como tudo funciona.

    E se você precisar ficar um pouco mais granular, com CSS Containment, você pode pular manualmente layout, estilo e trabalho de pintura para descendentes de um nó DOM se você precisar apenas de tamanho, alinhamento ou estilos computados em outros elementos - ou o elemento está atualmente fora da tela.

O desempenho de renderização no carregamento inicial é de 2.288 ms para linha de base (esquerda) e 13.464 ms para partes com visibilidade de conteúdo:auto (direita)
Na demonstração, aplicar content-visibility: auto a áreas de conteúdo em partes oferece um aumento de 7 vezes no desempenho de renderização no carregamento inicial. (Visualização grande)
  1. Você adia a decodificação com decoding="async" ?
    Às vezes, o conteúdo aparece fora da tela, mas queremos garantir que ele esteja disponível quando os clientes precisarem — idealmente, não bloqueando nada no caminho crítico, mas decodificando e renderizando de forma assíncrona. Podemos usar decoding="async" para dar ao navegador permissão para decodificar a imagem do thread principal, evitando o impacto do usuário do tempo de CPU usado para decodificar a imagem (via Malte Ubl):

    <img decoding="async" … />

    Alternativamente, para imagens fora da tela, podemos exibir primeiro um espaço reservado e, quando a imagem estiver dentro da janela de visualização, usando IntersectionObserver, acionar uma chamada de rede para que a imagem seja baixada em segundo plano. Além disso, podemos adiar a renderização até a decodificação com img.decode() ou baixar a imagem se a API de decodificação de imagem não estiver disponível.

    Ao renderizar a imagem, podemos usar animações de fade-in, por exemplo. Katie Hempenius e Addy Osmani compartilham mais insights em sua palestra Speed ​​at Scale: Web Performance Tips and Tricks from the Trenches.

  2. Você gera e serve CSS crítico?
    Para garantir que os navegadores comecem a renderizar sua página o mais rápido possível, tornou-se uma prática comum coletar todo o CSS necessário para começar a renderizar a primeira parte visível da página (conhecida como "CSS crítico" ou "CSS acima da dobra ") e incluí-lo inline no <head> da página, reduzindo assim as viagens de ida e volta. Devido ao tamanho limitado dos pacotes trocados durante a fase de início lento, seu orçamento para CSS crítico é de cerca de 14 KB.

    Se você for além disso, o navegador precisará de viagens de ida e volta adicionais para buscar mais estilos. CriticalCSS e Critical permitem que você produza CSS crítico para cada modelo que estiver usando. Em nossa experiência, porém, nenhum sistema automático foi melhor do que a coleta manual de CSS crítico para cada modelo e, de fato, essa é a abordagem para a qual voltamos recentemente.

    Você pode então inline CSS crítico e carregar lentamente o resto com o plugin critters Webpack. Se possível, considere usar a abordagem de inlining condicional usada pelo Filament Group ou converta o código inline em ativos estáticos em tempo real.

    Se você atualmente carrega seu CSS completo de forma assíncrona com bibliotecas como loadCSS, isso não é realmente necessário. Com media="print" , você pode enganar o navegador para buscar o CSS de forma assíncrona, mas aplicar ao ambiente da tela assim que ele for carregado. ( obrigado, Scott! )

    <!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />

    Ao coletar todo o CSS crítico para cada modelo, é comum explorar apenas a área "acima da dobra". No entanto, para layouts complexos, pode ser uma boa ideia incluir a base do layout também para evitar custos massivos de recálculo e repintura, prejudicando sua pontuação no Core Web Vitals como resultado.

    E se um usuário obtiver um URL que está direcionando diretamente para o meio da página, mas o CSS ainda não foi baixado? Nesse caso, tornou-se comum ocultar conteúdo não crítico, por exemplo, com opacity: 0; em CSS embutido e opacity: 1 em arquivo CSS completo e exibi-lo quando CSS estiver disponível. No entanto, ele tem uma grande desvantagem , pois os usuários em conexões lentas podem nunca conseguir ler o conteúdo da página. É por isso que é melhor sempre manter o conteúdo visível, mesmo que não esteja estilizado corretamente.

    Colocar CSS crítico (e outros ativos importantes) em um arquivo separado no domínio raiz traz benefícios, às vezes até mais do que inlining, devido ao cache. O Chrome abre especulativamente uma segunda conexão HTTP com o domínio raiz ao solicitar a página, o que elimina a necessidade de uma conexão TCP para buscar esse CSS. Isso significa que você pode criar um conjunto de arquivos -CSS críticos (por exemplo, critical-homepage.css , critical-product-page.css etc.) e servi-los de sua raiz, sem ter que inline-los. ( obrigado, Felipe! )

    Uma palavra de cautela: com HTTP/2, CSS crítico pode ser armazenado em um arquivo CSS separado e entregue por meio de um servidor push sem sobrecarregar o HTML. O problema é que o push do servidor foi problemático com muitas pegadinhas e condições de corrida nos navegadores. Ele nunca foi suportado de forma consistente e teve alguns problemas de cache (veja o slide 114 em diante da apresentação de Hooman Beheshti).

    O efeito pode, de fato, ser negativo e sobrecarregar os buffers da rede, impedindo que quadros genuínos no documento sejam entregues. Portanto, não foi muito surpreendente que, por enquanto, o Chrome esteja planejando remover o suporte ao Server Push.

  3. Experimente reagrupar suas regras CSS.
    Estamos acostumados com CSS crítico, mas existem algumas otimizações que podem ir além disso. Harry Roberts conduziu uma pesquisa notável com resultados bastante surpreendentes. Por exemplo, pode ser uma boa ideia dividir o arquivo CSS principal em suas consultas de mídia individuais. Dessa forma, o navegador recuperará CSS crítico com alta prioridade e todo o resto com baixa prioridade - completamente fora do caminho crítico.

    Além disso, evite colocar <link rel="stylesheet" /> antes de trechos async . Se os scripts não dependerem de folhas de estilo, considere colocar scripts de bloqueio acima dos estilos de bloqueio. Se o fizerem, divida esse JavaScript em dois e carregue-o em ambos os lados do seu CSS.

    Scott Jehl resolveu outro problema interessante armazenando em cache um arquivo CSS embutido com um service worker, um problema comum familiar se você estiver usando CSS crítico. Basicamente, adicionamos um atributo de ID ao elemento de style para que seja fácil encontrá-lo usando JavaScript, então um pequeno pedaço de JavaScript encontra esse CSS e usa a API de cache para armazená-lo em um cache de navegador local (com um tipo de conteúdo de text/css ) para uso nas páginas subsequentes. Para evitar o inline nas páginas subsequentes e, em vez disso, referenciar os ativos armazenados em cache externamente, definimos um cookie na primeira visita a um site. Voilá!

    Vale a pena notar que o estilo dinâmico também pode ser caro, mas geralmente apenas nos casos em que você confia em centenas de componentes compostos renderizados simultaneamente. Portanto, se você estiver usando CSS-in-JS, certifique-se de que sua biblioteca CSS-in-JS otimize a execução quando seu CSS não tiver dependências de tema ou adereços e não sobreponha componentes estilizados . Aggelos Arvanitakis compartilha mais insights sobre os custos de desempenho do CSS-in-JS.

  4. Você transmite respostas?
    Frequentemente esquecidos e negligenciados, os fluxos fornecem uma interface para leitura ou gravação de blocos de dados assíncronos, dos quais apenas um subconjunto pode estar disponível na memória a qualquer momento. Basicamente, eles permitem que a página que fez a solicitação original comece a trabalhar com a resposta assim que o primeiro bloco de dados estiver disponível e use analisadores otimizados para streaming para exibir progressivamente o conteúdo.

    Poderíamos criar um fluxo de várias fontes. Por exemplo, em vez de servir um shell de interface do usuário vazio e permitir que o JavaScript o preencha, você pode permitir que o service worker construa um fluxo em que o shell vem de um cache, mas o corpo vem da rede. Como Jeff Posnick observou, se o seu aplicativo da web é alimentado por um CMS que renderiza HTML pelo servidor juntando modelos parciais, esse modelo se traduz diretamente no uso de respostas de streaming, com a lógica de modelagem replicada no service worker em vez de no servidor. O artigo The Year of Web Streams de Jake Archibald destaca como exatamente você pode construí-lo. O aumento de desempenho é bastante perceptível.

    Uma vantagem importante de transmitir toda a resposta HTML é que o HTML renderizado durante a solicitação de navegação inicial pode aproveitar ao máximo o analisador HTML de streaming do navegador. Pedaços de HTML que são inseridos em um documento após o carregamento da página (como é comum com conteúdo preenchido via JavaScript) não podem aproveitar essa otimização.

    Suporte ao navegador? Ainda chegando lá com suporte parcial no Chrome, Firefox, Safari e Edge suportando a API e Service Workers sendo suportados em todos os navegadores modernos. E se você se sentir aventureiro novamente, pode verificar uma implementação experimental de solicitações de streaming, que permite começar a enviar a solicitação enquanto ainda gera o corpo. Disponível no Chrome 85.

Uma imagem resumindo o uso de dados salvos no Android Chrome e a média de acessos ou sessões de img descobertos pela pesquisa da Cloudinary em novembro de 2019 e abril de 2020
18% dos usuários globais do Android Chrome têm o Modo Lite ativado (também conhecido como Save-Data), de acordo com a pesquisa da Cloudinary. (Visualização grande)
  1. Considere tornar seus componentes com reconhecimento de conexão.
    Os dados podem ser caros e com carga útil crescente, precisamos respeitar os usuários que optam por optar pela economia de dados ao acessar nossos sites ou aplicativos. O cabeçalho de solicitação de dica do cliente Save-Data nos permite personalizar o aplicativo e a carga útil para usuários com restrições de custo e desempenho.

    Na verdade, você pode reescrever solicitações de imagens de alto DPI para imagens de baixo DPI, remover fontes da Web, efeitos de paralaxe sofisticados, miniaturas de visualização e rolagem infinita, desativar a reprodução automática de vídeo, envios de servidor, reduzir o número de itens exibidos e diminuir a qualidade da imagem ou até mesmo alterar a forma como você entrega marcação. Tim Vereecke publicou um artigo muito detalhado sobre estratégias de data-s(h)aver apresentando muitas opções para economia de dados.

    Quem está usando save-data , você pode estar se perguntando? 18% dos usuários globais do Android Chrome têm o Modo Lite ativado (com Save-Data ativado) e o número provavelmente será maior. De acordo com a pesquisa de Simon Hearne, a taxa de adesão é mais alta em dispositivos mais baratos, mas há muitos valores discrepantes. Por exemplo: os usuários no Canadá têm uma taxa de adesão de mais de 34% (em comparação com ~7% nos EUA) e os usuários do mais recente carro-chefe da Samsung têm uma taxa de adesão de quase 18% globalmente.

    Com o modo Save-Data ativado, o Chrome Mobile fornecerá uma experiência otimizada, ou seja, uma experiência da Web com proxy com scripts adiados , font-display: swap e carregamento lento forçado. É mais sensato construir a experiência por conta própria, em vez de depender do navegador para fazer essas otimizações.

    Atualmente, o cabeçalho é suportado apenas no Chromium, na versão Android do Chrome ou por meio da extensão Data Saver em um dispositivo desktop. Por fim, você também pode usar a API de informações de rede para fornecer módulos JavaScript caros, imagens e vídeos de alta resolução com base no tipo de rede. A API de informações de rede e especificamente navigator.connection.effectiveType usam valores RTT , downlink , effectiveType (e alguns outros) para fornecer uma representação da conexão e dos dados que os usuários podem manipular.

    Nesse contexto, Max Bock fala de componentes com reconhecimento de conexão e Addy Osmani fala de serviço de módulo adaptável. Por exemplo, com o React, poderíamos escrever um componente que renderiza de forma diferente para diferentes tipos de conexão. Como Max sugeriu, um componente <Media /> em um artigo de notícias pode gerar:

    • Offline : um espaço reservado com texto alt ,
    • 2G / modo save-data : uma imagem de baixa resolução,
    • 3G em tela não Retina: uma imagem de resolução média,
    • 3G em telas Retina: imagem Retina de alta resolução,
    • 4G : um vídeo HD.

    Dean Hume fornece uma implementação prática de uma lógica semelhante usando um service worker. Para um vídeo, podemos exibir um pôster de vídeo por padrão e, em seguida, exibir o ícone "Reproduzir", bem como o shell do player de vídeo, metadados do vídeo etc. em conexões melhores. Como alternativa para navegadores não compatíveis, poderíamos ouvir o evento canplaythrough e usar Promise.race() para expirar o carregamento da fonte se o evento canplaythrough não disparar em 2 segundos.

    Se você quiser se aprofundar um pouco mais, aqui estão alguns recursos para começar:

    • Addy Osmani mostra como implementar a veiculação adaptativa no React.
    • React Adaptive Loading Hooks & Utilities fornece trechos de código para React,
    • Netanel Basel explora componentes com reconhecimento de conexão em Angular,
    • Theodore Vorilas compartilha como funciona Servir Componentes Adaptativos Usando a API de Informações de Rede no Vue.
    • Umar Hansa mostra como baixar/executar seletivamente JavaScript caro.
  2. Considere tornar seu dispositivo de componentes com reconhecimento de memória.
    A conexão de rede nos dá apenas uma perspectiva no contexto do usuário. Indo além, você também pode ajustar dinamicamente os recursos com base na memória disponível do dispositivo, com a API de memória do dispositivo. navigator.deviceMemory returns how much RAM the device has in gigabytes, rounded down to the nearest power of two. The API also features a Client Hints Header, Device-Memory , that reports the same value.

    Bonus : Umar Hansa shows how to defer expensive scripts with dynamic imports to change the experience based on device memory, network connectivity and hardware concurrency.

A break-down showing how different resources are prioritized in Blink as of Chrome 46 and beyond
A break-down showing how different resources are prioritized in Blink as of Chrome 46 and beyond. (Image credit: Addy Osmani) (Large preview)
  1. Warm up the connection to speed up delivery.
    Use resource hints to save time on dns-prefetch (which performs a DNS lookup in the background), preconnect (which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background), prefetch (which asks the browser to request a resource) and preload (which prefetches resources without executing them, among other things). Well supported in modern browsers, with support coming to Firefox soon.

    Remember prerender ? The resource hint used to prompt browser to build out the entire page in the background for next navigation. The implementations issues were quite problematic, ranging from a huge memory footprint and bandwidth usage to multiple registered analytics hits and ad impressions.

    Unsurprinsingly, it was deprecated, but the Chrome team has brought it back as NoState Prefetch mechanism. In fact, Chrome treats the prerender hint as a NoState Prefetch instead, so we can still use it today. As Katie Hempenius explains in that article, "like prerendering, NoState Prefetch fetches resources in advance ; but unlike prerendering, it does not execute JavaScript or render any part of the page in advance."

    NoState Prefetch only uses ~45MiB of memory and subresources that are fetched will be fetched with an IDLE Net Priority. Since Chrome 69, NoState Prefetch adds the header Purpose: Prefetch to all requests in order to make them distinguishable from normal browsing.

    Also, watch out for prerendering alternatives and portals, a new effort toward privacy-conscious prerendering, which will provide the inset preview of the content for seamless navigations.

    Using resource hints is probably the easiest way to boost performance , and it works well indeed. When to use what? As Addy Osmani has explained, it's reasonable to preload resources that we know are very likely to be used on the current page and for future navigations across multiple navigation boundaries, eg Webpack bundles needed for pages the user hasn't visited yet.

    Addy's article on "Loading Priorities in Chrome" shows how exactly Chrome interprets resource hints, so once you've decided which assets are critical for rendering, you can assign high priority to them. To see how your requests are prioritized, you can enable a "priority" column in the Chrome DevTools network request table (as well as Safari).

    Most of the time these days, we'll be using at least preconnect and dns-prefetch , and we'll be cautious with using prefetch , preload and prerender . Note that even with preconnect and dns-prefetch , the browser has a limit on the number of hosts it will look up/connect to in parallel, so it's a safe bet to order them based on priority ( thanks Philip Tellis! ).

    Since fonts usually are important assets on a page, sometimes it's a good idea to request the browser to download critical fonts with preload . However, double check if it actually helps performance as there is a puzzle of priorities when preloading fonts: as preload is seen as high importance, it can leapfrog even more critical resources like critical CSS. ( thanks, Barry! )

    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />

    Since <link rel="preload"> accepts a media attribute, you could choose to selectively download resources based on @media query rules, as shown above.

    Furthermore, we can use imagesrcset and imagesizes attributes to preload late-discovered hero images faster, or any images that are loaded via JavaScript, eg movie posters:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>

    We can also preload the JSON as fetch , so it's discovered before JavaScript gets to request it:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>

    We could also load JavaScript dynamically, effectively for lazy execution of the script.

    /* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);

    A few gotchas to keep in mind: preload is good for moving the start download time of an asset closer to the initial request, but preloaded assets land in the memory cache which is tied to the page making the request. preload plays well with the HTTP cache: a network request is never sent if the item is already there in the HTTP cache.

    Hence, it's useful for late-discovered resources, hero images loaded via background-image , inlining critical CSS (or JavaScript) and pre-loading the rest of the CSS (or JavaScript).

    An example using the cover of the Greyhound movie starring Tom Hanks to show that preloaded images load earlier as there is no need to wait on JavaScript to discover
    Preload important images early; no need to wait on JavaScript to discover them. (Image credit: “Preload Late-Discovered Hero Images Faster” by Addy Osmani) (Large preview)

    A preload tag can initiate a preload only after the browser has received the HTML from the server and the lookahead parser has found the preload tag. Preloading via the HTTP header could be a bit faster since we don't to wait for the browser to parse the HTML to start the request (it's debated though).

    Early Hints will help even further, enabling preload to kick in even before the response headers for the HTML are sent (on the roadmap in Chromium, Firefox). Plus, Priority Hints will help us indicate loading priorities for scripts.

    Beware : if you're using preload , as must be defined or nothing loads, plus preloaded fonts without the crossorigin attribute will double fetch. If you're using prefetch , beware of the Age header issues in Firefox.

A graph showing first contentful paint (by server worker status) with count from 0 to 150 across a given period of time (in ms)
With a service worker, we can request just the bare minimum of data, and then transform that data into a full HTML document to improve FCP. (via Phil Walton) (Large preview)
  1. Use service workers for caching and network fallbacks.
    No performance optimization over a network can be faster than a locally stored cache on a user's machine (there are exceptions though). If your website is running over HTTPS, we can cache static assets in a service worker cache and store offline fallbacks (or even offline pages) and retrieve them from the user's machine, rather than going to the network.

    As suggested by Phil Walton, with service workers, we can send smaller HTML payloads by programmatically generating our responses. A service worker can request just the bare minimum of data it needs from the server (eg an HTML content partial, a Markdown file, JSON data, etc.), and then it can programmatically transform that data into a full HTML document. So once a user visits a site and the service worker is installed, the user will never request a full HTML page again. The performance impact can be quite impressive.

    Browser support? Service workers are widely supported and the fallback is the network anyway. Does it help boost performance ? Oh yes, it does. And it's getting better, eg with Background Fetch allowing background uploads/downloads via a service worker as well.

    There are a number of use cases for a service worker. For example, you could implement "Save for offline" feature, handle broken images, introduce messaging between tabs or provide different caching strategies based on request types. In general, a common reliable strategy is to store the app shell in the service worker's cache along with a few critical pages, such as offline page, frontpage and anything else that might be important in your case.

    There are a few gotchas to keep in mind though. With a service worker in place, we need to beware range requests in Safari (if you are using Workbox for a service worker it has a range request module). If you ever stumbled upon DOMException: Quota exceeded. error in the browser console, then look into Gerardo's article When 7KB equals 7MB.

    Como Gerardo escreve: “Se você estiver criando um aplicativo da Web progressivo e estiver experimentando armazenamento em cache inchado quando seu service worker armazena em cache ativos estáticos servidos de CDNs, certifique-se de que o cabeçalho de resposta CORS adequado exista para recursos de origem cruzada, você não armazena em cache respostas opacas com seu service worker inadvertidamente, você aceita ativos de imagem de origem cruzada no modo crossorigin adicionando o atributo de origem cruzada à tag <img> .”

    Há muitos recursos excelentes para começar a trabalhar com service workers:

    • Service Worker Mindset, que ajuda você a entender como os service workers trabalham nos bastidores e o que entender ao construir um.
    • Chris Ferdinandi fornece uma grande série de artigos sobre service workers, explicando como criar aplicativos offline e cobrindo uma variedade de cenários, desde salvar páginas visualizadas recentemente offline até definir uma data de expiração para itens em um cache de service worker.

    • Armadilhas e práticas recomendadas do Service Worker, com algumas dicas sobre o escopo, atraso no registro de um service worker e no cache do service worker.
    • Ótima série de Ire Aderinokun em "Offline First" com Service Worker, com uma estratégia de pré-cache no shell do aplicativo.
    • Service Worker: uma introdução com dicas práticas sobre como usar o service worker para experiências offline avançadas, sincronizações periódicas em segundo plano e notificações push.
    • Sempre vale a pena consultar o livro de receitas offline do bom e velho Jake Archibald, com várias receitas sobre como preparar seu próprio service worker.
    • Workbox é um conjunto de bibliotecas de service worker criadas especificamente para criar aplicativos da Web progressivos.
  2. Você está executando servidores de trabalho no CDN/Edge, por exemplo, para testes A/B?
    Neste ponto, estamos bastante acostumados a executar service workers no cliente, mas com CDNs implementando-os no servidor, podemos usá-los para ajustar o desempenho na borda também.

    Por exemplo, em testes A/B, quando o HTML precisa variar seu conteúdo para diferentes usuários, podemos usar Service Workers nos servidores CDN para lidar com a lógica. Também podemos transmitir a reescrita de HTML para acelerar sites que usam o Google Fonts.

Um gráfico mostrando séries temporais de instalações de service workers em desktops e dispositivos móveis com porcentagem de páginas ao longo do tempo entre janeiro de 2016 e julho de 2020
Séries temporais da instalação do service worker. Apenas 0,87% de todas as páginas de desktop registram um service worker, de acordo com o Web Almanac. (Visualização grande)
  1. Otimize o desempenho de renderização.
    Sempre que o aplicativo está lento, é perceptível imediatamente. Portanto, precisamos ter certeza de que não há atraso ao rolar a página ou quando um elemento é animado e que você está atingindo consistentemente 60 quadros por segundo. Se isso não for possível, pelo menos tornar os quadros por segundo consistentes é preferível a um intervalo misto de 60 a 15. Use o CSS' will-change para informar ao navegador quais elementos e propriedades serão alterados.

    Sempre que estiver enfrentando, depure repinturas desnecessárias no DevTools:

    • Meça o desempenho de renderização em tempo de execução. Confira algumas dicas úteis sobre como entender isso.
    • Para começar, verifique o curso gratuito Udacity de Paul Lewis sobre otimização de renderização de navegador e o artigo de Georgy Marchuk sobre pintura de navegador e considerações para desempenho na web.
    • Ative o Paint Flashing em "Mais ferramentas → Renderização → Paint Flashing" no Firefox DevTools.
    • No React DevTools, marque "Destacar atualizações" e ative "Registrar por que cada componente foi renderizado",
    • Você também pode usar Why Did You Render, então quando um componente for renderizado novamente, um flash irá notificá-lo sobre a mudança.

    Você está usando um layout de alvenaria? Tenha em mente que pode ser possível construir um layout Masonry apenas com grade CSS, muito em breve.

    Se você quiser se aprofundar no tópico, Nolan Lawson compartilhou truques para medir com precisão o desempenho do layout em seu artigo, e Jason Miller também sugeriu técnicas alternativas. Também temos um pequeno artigo de Sergey Chikuyonok sobre como obter a animação GPU certa.

    Animações de alto desempenho, incluindo Posição, Escala, Rotação e Opacidade
    Os navegadores podem animar a transformação e a opacidade de forma barata. CSS Triggers é útil para verificar se o CSS aciona re-layouts ou reflows. (Crédito da imagem: Addy Osmani)(Grande visualização)

    Nota : as alterações nas camadas compostas por GPU são as menos caras, portanto, se você puder se safar acionando apenas a composição via opacity e transform , estará no caminho certo. Anna Migas também forneceu muitos conselhos práticos em sua palestra sobre Depuração do desempenho de renderização da interface do usuário. E para entender como depurar o desempenho da pintura no DevTools, confira o vídeo de auditoria de desempenho da pintura de Umar.

  2. Você otimizou para o desempenho percebido?
    Embora a sequência de como os componentes aparecem na página e a estratégia de como fornecemos recursos para o navegador sejam importantes, também não devemos subestimar o papel do desempenho percebido. O conceito lida com aspectos psicológicos da espera, basicamente mantendo os clientes ocupados ou engajados enquanto outra coisa está acontecendo. É aí que entram em jogo a gestão da percepção, o início preventivo, a conclusão antecipada e a gestão da tolerância.

    O que tudo isso significa? Ao carregar ativos, podemos tentar estar sempre um passo à frente do cliente, para que a experiência pareça rápida enquanto há muita coisa acontecendo em segundo plano. Para manter o cliente engajado, podemos testar telas de esqueleto (demonstração de implementação) ao invés de indicadores de carregamento, adicionar transições/animações e basicamente enganar o UX quando não há mais nada para otimizar.

    Em seu estudo de caso sobre The Art of UI Skeletons, Kumar McMillan compartilha algumas ideias e técnicas sobre como simular listas dinâmicas, texto e a tela final, bem como considerar o pensamento de esqueleto com o React.

    Cuidado, porém: as telas de esqueleto devem ser testadas antes da implantação, pois alguns testes mostraram que as telas de esqueleto podem ter o pior desempenho em todas as métricas.

  3. Você evita mudanças de layout e repinturas?
    No domínio do desempenho percebido, provavelmente uma das experiências mais disruptivas é a mudança de layout , ou refluxo , causada por imagens e vídeos redimensionados, fontes da Web, anúncios injetados ou scripts descobertos tardiamente que preenchem componentes com conteúdo real. Como resultado, um cliente pode começar a ler um artigo apenas para ser interrompido por um salto de layout acima da área de leitura. A experiência é muitas vezes abrupta e bastante desorientadora: e esse é provavelmente um caso de prioridades de carregamento que precisam ser reconsideradas.

    A comunidade desenvolveu algumas técnicas e soluções alternativas para evitar refluxos. Em geral, é uma boa ideia evitar a inserção de novo conteúdo acima do conteúdo existente , a menos que isso aconteça em resposta a uma interação do usuário. Sempre defina os atributos de largura e altura nas imagens, para que os navegadores modernos atribuam a caixa e reservem o espaço por padrão (Firefox, Chrome).

    Tanto para imagens quanto para vídeos, podemos usar um espaço reservado SVG para reservar a caixa de exibição na qual a mídia aparecerá. Isso significa que a área será reservada adequadamente quando você precisar manter sua proporção também. Também podemos usar marcadores de posição ou imagens substitutas para anúncios e conteúdo dinâmico, bem como pré-alocar espaços de layout.

    Em vez de imagens de carregamento lento com scripts externos, considere usar carregamento lento nativo ou carregamento lento híbrido quando carregarmos um script externo somente se o carregamento lento nativo não for suportado.

    Como mencionado acima, sempre agrupe repinturas de fontes da Web e faça a transição de todas as fontes de fallback para todas as fontes da Web de uma só vez - apenas certifique-se de que essa mudança não seja muito abrupta, ajustando a altura da linha e o espaçamento entre as fontes com o font-style-matcher .

    Para substituir as métricas de fonte de uma fonte substituta para emular uma fonte da Web, podemos usar os descritores @font-face para substituir as métricas de fonte (demo, ativado no Chrome 87). (Observe que os ajustes são complicados com pilhas de fontes complicadas.)

    Para CSS tardio, podemos garantir que o CSS de layout crítico seja embutido no cabeçalho de cada modelo. Além disso: para páginas longas, quando a barra de rolagem vertical é adicionada, ela desloca o conteúdo principal 16px para a esquerda. Para exibir uma barra de rolagem antecipadamente, podemos adicionar overflow-y: scroll on html para impor uma barra de rolagem na primeira pintura. O último ajuda porque as barras de rolagem podem causar mudanças de layout não triviais devido ao refluxo do conteúdo acima da dobra quando a largura muda. Deve acontecer principalmente em plataformas com barras de rolagem sem sobreposição, como o Windows. Mas: break position: sticky porque esses elementos nunca rolarão para fora do contêiner.

    Se você lida com cabeçalhos que se tornam fixos ou fixos posicionados no topo da página na rolagem, reserve espaço para o cabeçalho quando ele se tornar pinado, por exemplo, com um elemento de espaço reservado ou margin-top no conteúdo. Uma exceção deve ser banners de consentimento de cookies que não devem ter impacto no CLS, mas às vezes eles têm: depende da implementação. Existem algumas estratégias e conclusões interessantes neste tópico do Twitter.

    Para um componente de guia que pode incluir várias quantidades de textos, você pode evitar mudanças de layout com pilhas de grade CSS. Ao colocar o conteúdo de cada guia na mesma área da grade e ocultar uma delas por vez, podemos garantir que o contêiner sempre tenha a altura do elemento maior, para que não ocorram mudanças de layout.

    Ah, e claro, rolagem infinita e "Carregar mais" também podem causar mudanças de layout se houver conteúdo abaixo da lista (por exemplo, rodapé). Para melhorar o CLS, reserve espaço suficiente para o conteúdo que seria carregado antes que o usuário role para essa parte da página, remova o rodapé ou qualquer elemento DOM na parte inferior da página que possa ser empurrado para baixo pelo carregamento do conteúdo. Além disso, pré-buscar dados e imagens para conteúdo abaixo da dobra para que, quando um usuário rolar até esse ponto, ele já esteja lá. Você pode usar bibliotecas de virtualização de listas como react-window para otimizar listas longas também ( obrigado, Addy Osmani! ).

    Para garantir que o impacto dos refluxos seja contido, meça a estabilidade do layout com a Layout Instability API. Com ele, você pode calcular a pontuação Cumulative Layout Shift ( CLS ) e incluí-la como requisito em seus testes, assim sempre que uma regressão aparecer, você poderá rastreá-la e corrigi-la.

    Para calcular a pontuação de mudança de layout, o navegador analisa o tamanho da janela de visualização e o movimento de elementos instáveis ​​na janela de visualização entre dois quadros renderizados. Idealmente, a pontuação seria próxima de 0 . Há um ótimo guia de Milica Mihajlija e Philip Walton sobre o que é o CLS e como medi-lo. É um bom ponto de partida para medir e manter o desempenho percebido e evitar interrupções, especialmente para tarefas críticas para os negócios.

    Dica rápida : para descobrir o que causou uma mudança de layout no DevTools, você pode explorar as mudanças de layout em "Experiência" no Painel de desempenho.

    Bônus : se você deseja reduzir refluxos e repinturas, consulte o guia de Charis Theodoulou para Minimizing DOM Reflow/Layout Thrashing e a lista de Paul Irish de O que força layout/reflow, bem como CSSTriggers.com, uma tabela de referência sobre propriedades CSS que acionam layout, pintura e composição.

Rede e HTTP/2

  1. O grampeamento OCSP está ativado?
    Ao habilitar o grampeamento OCSP em seu servidor, você pode acelerar seus handshakes TLS. O Online Certificate Status Protocol (OCSP) foi criado como uma alternativa ao protocolo Certificate Revocation List (CRL). Ambos os protocolos são usados ​​para verificar se um certificado SSL foi revogado.

    No entanto, o protocolo OCSP não exige que o navegador gaste tempo baixando e pesquisando uma lista de informações de certificado, reduzindo assim o tempo necessário para um handshake.

  2. Você reduziu o impacto da revogação do certificado SSL?
    Em seu artigo sobre "O custo de desempenho dos certificados EV", Simon Hearne fornece uma ótima visão geral dos certificados comuns e o impacto que a escolha de um certificado pode ter no desempenho geral.

    Como Simon escreve, no mundo do HTTPS, existem alguns tipos de níveis de validação de certificado usados ​​para proteger o tráfego:

    • A Validação de Domínio (DV) valida se o solicitante do certificado possui o domínio,
    • A Validação da Organização (OV) valida que uma organização possui o domínio,
    • A Validação Estendida (EV) valida que uma organização possui o domínio, com validação rigorosa.

    É importante observar que todos esses certificados são iguais em termos de tecnologia; eles diferem apenas nas informações e propriedades fornecidas nesses certificados.

    Os certificados EV são caros e demorados , pois exigem que um humano revise um certificado e garanta sua validade. Os certificados DV, por outro lado, geralmente são fornecidos gratuitamente — por exemplo, pela Let's Encrypt — uma autoridade de certificação aberta e automatizada que está bem integrada a muitos provedores de hospedagem e CDNs. De fato, no momento da redação deste artigo, ele alimenta mais de 225 milhões de sites (PDF), embora represente apenas 2,69% das páginas (abertas no Firefox).

    Então qual é o problema? O problema é que os certificados EV não suportam totalmente o grampeamento OCSP mencionado acima. Enquanto o grampeamento permite que o servidor verifique com a Autoridade de Certificação se o certificado foi revogado e então adicione ("grampeie") essa informação ao certificado, sem grampear o cliente tem que fazer todo o trabalho, resultando em solicitações desnecessárias durante a negociação TLS . Em conexões ruins, isso pode resultar em custos de desempenho perceptíveis (1000ms+).

    Os certificados EV não são uma ótima opção para desempenho na Web e podem causar um impacto muito maior no desempenho do que os certificados DV. Para um desempenho ideal na Web, sempre forneça um certificado DV grampeado OCSP. Eles também são muito mais baratos que os certificados EV e menos incômodos para adquirir. Bem, pelo menos até que o CRLite esteja disponível.

    Um gráfico mostrando o número de handshakes ao longo de datagramas UDP no local em casos de simples, compactados ou ambos
    A compactação é importante: 40 a 43% das cadeias de certificados não compactadas são muito grandes para caber em um único voo QUIC de 3 datagramas UDP. (Crédito da imagem:) Fastly) (Visualização grande)

    Nota : Com o QUIC/HTTP/3 sobre nós, vale a pena notar que a cadeia de certificados TLS é o conteúdo de tamanho variável que domina a contagem de bytes no QUIC Handshake. O tamanho varia entre algumas centenas de byes e mais de 10 KB.

    Portanto, manter os certificados TLS pequenos é muito importante no QUIC/HTTP/3, pois certificados grandes causarão vários handshakes. Além disso, precisamos garantir que os certificados sejam compactados, caso contrário, as cadeias de certificados seriam muito grandes para caber em um único voo QUIC.

    Você pode encontrar muito mais detalhes e indicadores para o problema e as soluções em:

    • Certificados EV tornam a Web lenta e não confiável por Aaron Peters,
    • O impacto da revogação do certificado SSL no desempenho da Web por Matt Hobbs,
    • O custo de desempenho dos certificados EV por Simon Hearne,
    • O handshake QUIC exige que a compactação seja rápida? por Patrick McManus.
  3. Você já adotou o IPv6?
    Como estamos ficando sem espaço com o IPv4 e as principais redes móveis estão adotando o IPv6 rapidamente (os EUA quase atingiram um limite de adoção de 50% do IPv6), é uma boa ideia atualizar seu DNS para IPv6 para ficar à prova de balas para o futuro. Apenas certifique-se de que o suporte de pilha dupla seja fornecido em toda a rede - ele permite que o IPv6 e o ​​IPv4 sejam executados simultaneamente um ao lado do outro. Afinal, o IPv6 não é compatível com versões anteriores. Além disso, estudos mostram que o IPv6 tornou esses sites 10 a 15% mais rápidos devido à descoberta de vizinhos (NDP) e à otimização de rotas.
  4. Certifique-se de que todos os ativos sejam executados em HTTP/2 (ou HTTP/3).
    Com o Google buscando uma web HTTPS mais segura nos últimos anos, uma mudança para o ambiente HTTP/2 é definitivamente um bom investimento. De fato, de acordo com o Web Almanac, 64% de todas as solicitações já estão sendo executadas em HTTP/2.

    É importante entender que o HTTP/2 não é perfeito e tem problemas de priorização, mas é muito bem suportado; e, na maioria dos casos, você está melhor com isso.

    Uma palavra de cautela: o HTTP/2 Server Push está sendo removido do Chrome, portanto, se sua implementação depender do Server Push, talvez seja necessário revisitá-lo. Em vez disso, podemos estar olhando para as dicas iniciais, que já estão integradas como experimentos no Fastly.

    Se você ainda estiver executando em HTTP, a tarefa mais demorada será migrar primeiro para HTTPS e, em seguida, ajustar seu processo de compilação para atender à multiplexação e paralelização HTTP/2. Trazer HTTP/2 para Gov.uk é um estudo de caso fantástico sobre como fazer exatamente isso, encontrando um caminho através de CORS, SRI e WPT ao longo do caminho. Para o restante deste artigo, presumimos que você está mudando ou já mudou para HTTP/2.

Um gráfico mostrando a série temporal de solicitações HTTP/2 em computadores e dispositivos móveis de 2 de janeiro de 2016 a 1º de outubro de 2020
64% de todas as solicitações são atendidas por HTTP/2 no final de 2020, de acordo com o Web Almanac — apenas 4 anos após sua padronização formal. (Fonte da imagem: Web Almanac) (Visualização grande)
  1. Implante corretamente o HTTP/2.
    Novamente, servir ativos por HTTP/2 pode se beneficiar de uma revisão parcial de como você tem servido ativos até agora. Você precisará encontrar um bom equilíbrio entre os módulos de empacotamento e o carregamento de muitos módulos pequenos em paralelo. No final das contas, a melhor solicitação ainda é nenhuma solicitação, no entanto, o objetivo é encontrar um bom equilíbrio entre a primeira entrega rápida de ativos e o armazenamento em cache.

    Por um lado, você pode querer evitar a concatenação de ativos completamente, em vez de dividir sua interface inteira em vários módulos pequenos, compactando-os como parte do processo de compilação e carregando-os em paralelo. Uma alteração em um arquivo não exigirá que toda a folha de estilo ou JavaScript seja baixado novamente. Também minimiza o tempo de análise e mantém baixas as cargas úteis de páginas individuais.

    Por outro lado, a embalagem ainda importa. Ao usar muitos scripts pequenos, a compactação geral sofrerá e o custo de recuperar objetos do cache aumentará. A compactação de um pacote grande se beneficiará da reutilização do dicionário, enquanto pequenos pacotes separados não. Há um trabalho padrão para resolver isso, mas está longe por enquanto. Em segundo lugar, os navegadores ainda não foram otimizados para esses fluxos de trabalho. Por exemplo, o Chrome acionará comunicações entre processos (IPCs) lineares ao número de recursos, portanto, incluir centenas de recursos terá custos de tempo de execução do navegador.

    Código HTML usando carregamento CSS progressivo
    Para obter melhores resultados com HTTP/2, considere carregar CSS progressivamente, conforme sugerido por Jake Archibald do Chrome.

    Ainda assim, você pode tentar carregar CSS progressivamente. Na verdade, o CSS interno não bloqueia mais a renderização do Chrome. Mas existem alguns problemas de priorização, por isso não é tão simples, mas vale a pena experimentar.

    Você pode se safar com a união de conexão HTTP/2, que permite usar fragmentação de domínio enquanto se beneficia do HTTP/2, mas conseguir isso na prática é difícil e, em geral, não é considerado uma boa prática. Além disso, HTTP/2 e integridade de sub-recurso nem sempre funcionam.

    O que fazer? Bem, se você estiver executando em HTTP/2, enviar cerca de 6 a 10 pacotes parece um compromisso decente (e não é tão ruim para navegadores legados). Experimente e meça para encontrar o equilíbrio certo para o seu site.

  2. Enviamos todos os ativos em uma única conexão HTTP/2?
    Uma das principais vantagens do HTTP/2 é que ele nos permite enviar ativos pela rede em uma única conexão. No entanto, às vezes podemos ter feito algo errado - por exemplo, ter um problema de CORS ou configurar incorretamente o atributo crossorigin , de modo que o navegador seria forçado a abrir uma nova conexão.

    Para verificar se todas as requisições usam uma única conexão HTTP/2, ou algo está mal configurado, habilite a coluna "Connection ID" em DevTools → Network. Por exemplo, aqui, todas as solicitações compartilham a mesma conexão (286) — exceto manifest.json, que abre uma conexão separada (451).

Uma captura de tela do DevTools aberto no navegador Chrome
Todas as solicitações compartilham a mesma conexão HTTP/2 (286) — exceto manifest.json, que abre uma conexão separada (451). via Iamakulov. (Visualização grande)
  1. Seus servidores e CDNs suportam HTTP/2?
    Diferentes servidores e CDNs (ainda) suportam HTTP/2 de forma diferente. Use a Comparação de CDN para verificar suas opções ou veja rapidamente o desempenho de seus servidores e quais recursos você espera que sejam suportados.

    Consulte a incrível pesquisa de Pat Meenan sobre prioridades HTTP/2 (vídeo) e teste o suporte do servidor para priorização HTTP/2. De acordo com Pat, é recomendado habilitar o controle de congestionamento BBR e definir tcp_notsent_lowat para 16KB para que a priorização HTTP/2 funcione de forma confiável em kernels Linux 4.9 e posteriores ( obrigado, Yoav! ). Andy Davies fez uma pesquisa semelhante para priorização de HTTP/2 em navegadores, CDNs e serviços de hospedagem em nuvem.

    Enquanto estiver nele, verifique se seu kernel suporta TCP BBR e habilite-o se possível. Atualmente, é usado no Google Cloud Platform, Amazon Cloudfront, Linux (por exemplo, Ubuntu).

  2. A compactação HPACK está em uso?
    Se você estiver usando HTTP/2, verifique novamente se seus servidores implementam a compactação HPACK para cabeçalhos de resposta HTTP para reduzir a sobrecarga desnecessária. Alguns servidores HTTP/2 podem não suportar totalmente a especificação, sendo o HPACK um exemplo. O H2spec é uma ótima ferramenta (embora muito detalhada tecnicamente) para verificar isso. O algoritmo de compressão do HPACK é bastante impressionante e funciona.
  3. Certifique-se de que a segurança do seu servidor seja à prova de balas.
    Todas as implementações do HTTP/2 no navegador são executadas em TLS, portanto, você provavelmente desejará evitar avisos de segurança ou alguns elementos em sua página que não funcionem. Verifique novamente se seus cabeçalhos de segurança estão configurados corretamente, elimine vulnerabilidades conhecidas e verifique sua configuração HTTPS.

    Além disso, certifique-se de que todos os plug-ins externos e scripts de rastreamento sejam carregados via HTTPS, que scripts entre sites não sejam possíveis e que os cabeçalhos HTTP Strict Transport Security e Content Security Policy estejam configurados corretamente.

  4. Seus servidores e CDNs suportam HTTP/3?
    Embora o HTTP/2 tenha trazido uma série de melhorias significativas de desempenho para a web, também deixou bastante área para melhorias - especialmente o bloqueio de cabeçalho de linha no TCP, que era perceptível em uma rede lenta com uma perda significativa de pacotes. O HTTP/3 está resolvendo esses problemas definitivamente (artigo).

    Para resolver problemas de HTTP/2, o IETF, juntamente com Google, Akamai e outros, estão trabalhando em um novo protocolo que foi recentemente padronizado como HTTP/3.

    Robin Marx explicou o HTTP/3 muito bem, e a explicação a seguir é baseada em sua explicação. Em seu núcleo, o HTTP/3 é muito semelhante ao HTTP/2 em termos de recursos, mas nos bastidores funciona de maneira muito diferente. O HTTP/3 oferece várias melhorias: handshakes mais rápidos, melhor criptografia, fluxos independentes mais confiáveis, melhor criptografia e controle de fluxo. Uma diferença notável é que HTTP/3 usa QUIC como camada de transporte, com pacotes QUIC encapsulados em diagramas UDP, em vez de TCP.

    O QUIC integra totalmente o TLS 1.3 no protocolo, enquanto no TCP ele está em camadas. Na pilha TCP típica, temos alguns tempos de sobrecarga de ida e volta porque o TCP e o TLS precisam fazer seus próprios handshakes separados, mas com o QUIC ambos podem ser combinados e concluídos em apenas uma única viagem de ida e volta . Como o TLS 1.3 permite configurar chaves de criptografia para uma conexão consequente, a partir da segunda conexão já podemos enviar e receber dados da camada de aplicação na primeira ida e volta, que é chamada de "0-RTT".

    Além disso, o algoritmo de compressão de cabeçalho do HTTP/2 foi totalmente reescrito, juntamente com seu sistema de priorização. Além disso, o QUIC suporta migração de conexão de Wi-Fi para rede celular por meio de IDs de conexão no cabeçalho de cada pacote QUIC. A maioria das implementações é feita no espaço do usuário, não no espaço do kernel (como é feito com o TCP), então devemos esperar que o protocolo evolua no futuro.

    Tudo faria uma grande diferença? Provavelmente sim, especialmente tendo um impacto nos tempos de carregamento em dispositivos móveis, mas também em como servimos ativos para usuários finais. Enquanto no HTTP/2, várias solicitações compartilham uma conexão, em HTTP/3 as solicitações também compartilham uma conexão, mas transmitem independentemente, portanto, um pacote descartado não afeta mais todas as solicitações, apenas um fluxo.

    Isso significa que, embora com um grande pacote JavaScript o processamento de ativos seja desacelerado quando um fluxo pausar, o impacto será menos significativo quando vários arquivos forem transmitidos em paralelo (HTTP/3). Portanto , a embalagem ainda importa .

    O HTTP/3 ainda está em andamento. Chrome, Firefox e Safari já têm implementações. Alguns CDNs já suportam QUIC e HTTP/3. No final de 2020, o Chrome começou a implantar HTTP/3 e IETF QUIC e, de fato, todos os serviços do Google (Google Analytics, YouTube etc.) já estão sendo executados em HTTP/3. LiteSpeed ​​Web Server suporta HTTP/3, mas nem Apache, nginx ou IIS suportam ainda, mas é provável que mude rapidamente em 2021.

    Conclusão : se você tiver a opção de usar HTTP/3 no servidor e em seu CDN, provavelmente é uma boa ideia fazê-lo. O principal benefício virá da busca de vários objetos simultaneamente, especialmente em conexões de alta latência. Ainda não sabemos ao certo, pois não há muita pesquisa feita nesse espaço, mas os primeiros resultados são muito promissores.

    Se você quiser se aprofundar mais nas especificidades e vantagens do protocolo, aqui estão alguns bons pontos de partida a serem verificados:

    • HTTP/3 Explained, um esforço colaborativo para documentar os protocolos HTTP/3 e QUIC. Disponível em vários idiomas, também em PDF.
    • Nivelando o desempenho da Web com HTTP/3 com Daniel Stenberg.
    • Um Guia Acadêmico para QUIC com Robin Marx apresenta conceitos básicos dos protocolos QUIC e HTTP/3, explica como HTTP/3 lida com bloqueio de cabeçalho e migração de conexão e como HTTP/3 é projetado para ser perene (obrigado, Simon !).
    • Você pode verificar se seu servidor está rodando em HTTP/3 em HTTP3Check.net.

Teste e monitoramento

  1. Você otimizou seu fluxo de trabalho de auditoria?
    Pode não parecer grande coisa, mas ter as configurações corretas na ponta dos dedos pode economizar bastante tempo nos testes. Considere usar o Alfred Workflow de Tim Kadlec para WebPageTest para enviar um teste para a instância pública de WebPageTest. Na verdade, o WebPageTest tem muitos recursos obscuros, então reserve um tempo para aprender a ler um gráfico WebPageTest Waterfall View e como ler um gráfico WebPageTest Connection View para diagnosticar e resolver problemas de desempenho mais rapidamente.

    Você também pode conduzir o WebPageTest a partir de uma planilha do Google e incorporar pontuações de acessibilidade, desempenho e SEO em sua configuração do Travis com o Lighthouse CI ou diretamente no Webpack.

    Dê uma olhada no recém-lançado AutoWebPerf, uma ferramenta modular que permite a coleta automática de dados de desempenho de várias fontes. Por exemplo, poderíamos definir um teste diário em suas páginas críticas para capturar os dados de campo da API CrUX e dados de laboratório de um relatório do Lighthouse do PageSpeed ​​Insights.

    E se você precisar depurar algo rapidamente, mas seu processo de compilação parecer extremamente lento, lembre-se de que "a remoção de espaços em branco e a destruição de símbolos são responsáveis ​​por 95% da redução de tamanho no código minificado para a maioria dos JavaScript - não em transformações de código elaboradas. Você pode simplesmente desative a compactação para acelerar as compilações do Uglify em 3 a 4 vezes."

Uma captura de tela da notificação Pull Request do GitHub informando que a revisão é necessária e que a mesclagem está bloqueada até que as verificações sejam resolvidas com sucesso
A integração das pontuações de acessibilidade, desempenho e SEO em sua configuração do Travis com o Lighthouse CI destacará o impacto no desempenho de um novo recurso para todos os desenvolvedores contribuintes. (Fonte da imagem) (Visualização grande)
  1. Você testou em navegadores proxy e navegadores legados?
    Testar no Chrome e Firefox não é suficiente. Veja como seu site funciona em navegadores proxy e navegadores legados. O UC Browser e o Opera Mini, por exemplo, têm uma participação de mercado significativa na Ásia (até 35% na Ásia). Meça a velocidade média da Internet em seus países de interesse para evitar grandes surpresas no futuro. Teste com limitação de rede e emule um dispositivo de alto DPI. O BrowserStack é fantástico para testar em dispositivos reais remotos e complementá-lo com pelo menos alguns dispositivos reais em seu escritório – vale a pena.
  1. Você testou o desempenho de suas páginas 404?
    Normalmente não pensamos duas vezes quando se trata de 404 páginas. Afinal, quando um cliente solicita uma página que não existe no servidor, o servidor responderá com um código de status 404 e a página 404 associada. Não há muito para isso, não é?

    Um aspecto importante das respostas 404 é o tamanho real do corpo da resposta que está sendo enviada ao navegador. De acordo com a pesquisa de 404 páginas de Matt Hobbs, a grande maioria das 404 respostas são provenientes de favicons ausentes, solicitações de upload do WordPress, solicitações JavaScript quebradas, arquivos de manifesto, bem como arquivos CSS e de fonte. Toda vez que um cliente solicita um ativo que não existe, ele recebe uma resposta 404 – e geralmente essa resposta é enorme.

    Certifique-se de examinar e otimizar a estratégia de cache para suas páginas 404. Nosso objetivo é fornecer HTML ao navegador somente quando ele espera uma resposta HTML e retornar uma pequena carga útil de erro para todas as outras respostas. De acordo com Matt, "se colocarmos um CDN na frente de nossa origem, temos a chance de armazenar em cache a resposta de 404 páginas no CDN. Isso é útil porque sem ele, atingir uma página 404 poderia ser usado como um vetor de ataque DoS, por forçando o servidor de origem a responder a cada solicitação 404 em vez de deixar o CDN responder com uma versão em cache."

    Os erros 404 não apenas podem prejudicar seu desempenho, mas também podem custar em tráfego, portanto, é uma boa ideia incluir uma página de erro 404 em seu conjunto de testes do Lighthouse e acompanhar sua pontuação ao longo do tempo.

  2. Você testou o desempenho de suas solicitações de consentimento do GDPR?
    Em tempos de GDPR e CCPA, tornou-se comum depender de terceiros para fornecer opções para os clientes da UE optarem ou não pelo rastreamento. No entanto, como acontece com qualquer outro script de terceiros, seu desempenho pode ter um impacto bastante devastador em todo o esforço de desempenho.

    É claro que o consentimento real provavelmente alterará o impacto dos scripts no desempenho geral, portanto, como Boris Schapira observou, podemos estudar alguns perfis de desempenho da Web diferentes:

    • O consentimento foi totalmente recusado,
    • O consentimento foi parcialmente recusado,
    • O consentimento foi inteiramente dado.
    • O usuário não agiu no prompt de consentimento (ou o prompt foi bloqueado por um bloqueador de conteúdo),

    Normalmente, os prompts de consentimento de cookies não devem ter impacto no CLS, mas às vezes têm, portanto, considere usar as opções gratuitas e de código aberto Osano ou cookie-consent-box.

    Em geral, vale a pena examinar o desempenho do pop-up, pois você precisará determinar o deslocamento horizontal ou vertical do evento do mouse e posicionar corretamente o pop-up em relação à âncora. Noam Rosenthal compartilha os aprendizados da equipe da Wikimedia no artigo Estudo de caso de desempenho na Web: visualizações da página da Wikipedia (também disponível como vídeo e minutos).

  3. Você mantém um CSS de diagnóstico de desempenho?
    Embora possamos incluir todos os tipos de verificações para garantir que o código com baixo desempenho seja implantado, geralmente é útil ter uma ideia rápida de alguns dos frutos mais fáceis que podem ser resolvidos facilmente. Para isso, poderíamos usar o brilhante Performance Diagnostics CSS de Tim Kadlec (inspirado no trecho de Harry Roberts, que destaca imagens de carregamento lento, imagens sem tamanho, imagens de formato herdado e scripts síncronos.

    Por exemplo, você pode querer garantir que nenhuma imagem acima da dobra seja carregada lentamente. Você pode personalizar o snippet para suas necessidades, por exemplo, para destacar fontes da web que não são usadas ou detectar fontes de ícone. Uma ótima ferramenta para garantir que os erros sejam visíveis durante a depuração ou apenas para auditar o projeto atual muito rapidamente.

    /* Performance Diagnostics CSS */ /* via Harry Roberts. https://twitter.com/csswizardry/status/1346477682544951296 */ img[loading=lazy] { outline: 10px solid red; }
  1. Have you tested the impact on accessibility?
    When the browser starts to load a page, it builds a DOM, and if there is an assistive technology like a screen reader running, it also creates an accessibility tree. The screen reader then has to query the accessibility tree to retrieve the information and make it available to the user — sometimes by default, and sometimes on demand. And sometimes it takes time.

    When talking about fast Time to Interactive, usually we mean an indicator of how soon a user can interact with the page by clicking or tapping on links and buttons. The context is slightly different with screen readers. In that case, fast Time to Interactive means how much time passes by until the screen reader can announce navigation on a given page and a screen reader user can actually hit keyboard to interact.

    Leonie Watson has given an eye-opening talk on accessibility performance and specifically the impact slow loading has on screen reader announcement delays. Screen readers are used to fast-paced announcements and quick navigation, and therefore might potentially be even less patient than sighted users.

    Large pages and DOM manipulations with JavaScript will cause delays in screen reader announcements. A rather unexplored area that could use some attention and testing as screen readers are available on literally every platform (Jaws, NVDA, Voiceover, Narrator, Orca).

  2. Is continuous monitoring set up?
    Having a private instance of WebPagetest is always beneficial for quick and unlimited tests. However, a continuous monitoring tool — like Sitespeed, Calibre and SpeedCurve — with automatic alerts will give you a more detailed picture of your performance. Set your own user-timing marks to measure and monitor business-specific metrics. Also, consider adding automated performance regression alerts to monitor changes over time.

    Look into using RUM-solutions to monitor changes in performance over time. For automated unit-test-alike load testing tools, you can use k6 with its scripting API. Also, look into SpeedTracker, Lighthouse and Calibre.

Vitórias rápidas

This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let's boil it all down to 17 low-hanging fruits . Obviously, before you start and once you finish, measure results, including Largest Contentful Paint and Time To Interactive on a 3G and cable connection.

  1. Measure the real world experience and set appropriate goals. Aim to be at least 20% faster than your fastest competitor. Stay within Largest Contentful Paint < 2.5s, a First Input Delay < 100ms, Time to Interactive < 5s on slow 3G, for repeat visits, TTI < 2s. Optimize at least for First Contentful Paint and Time To Interactive.
  2. Optimize images with Squoosh, mozjpeg, guetzli, pingo and SVGOMG, and serve AVIF/WebP with an image CDN.
  3. Prepare critical CSS for your main templates, and inline them in the <head> of each template. For CSS/JS, operate within a critical file size budget of max. 170KB gzipped (0.7MB decompressed).
  4. Trim, optimize, defer and lazy-load scripts. Invest in the config of your bundler to remove redundancies and check lightweight alternatives.
  5. Always self-host your static assets and always prefer to self-host third-party assets. Limit the impact of third-party scripts. Use facades, load widgets on interaction and beware of anti-flicker snippets.
  6. Be selective when choosing a framework. For single-page-applications, identify critical pages and serve them statically, or at least prerender them, and use progressive hydration on component-level and import modules on interaction.
  7. Client-side rendering alone isn't a good choice for performance. Prerender if your pages don't change much, and defer the booting of frameworks if you can. If possible, use streaming server-side rendering.
  8. Serve legacy code only to legacy browsers with <script type="module"> and module/nomodule pattern.
  9. Experiment with regrouping your CSS rules and test in-body CSS.
  10. Add resource hints to speed up delivery with faster dns-lookup , preconnect , prefetch , preload and prerender .
  11. Subset web fonts and load them asynchronously, and utilize font-display in CSS for fast first rendering.
  12. Check that HTTP cache headers and security headers are set properly.
  13. Enable Brotli compression on the server. (If that's not possible, at least make sure that Gzip compression is enabled.)
  14. Enable TCP BBR congestion as long as your server is running on the Linux kernel version 4.9+.
  15. Enable OCSP stapling and IPv6 if possible. Always serve an OCSP stapled DV certificate.
  16. Enable HPACK compression for HTTP/2 and move to HTTP/3 if it's available.
  17. Cache assets such as fonts, styles, JavaScript and images in a service worker cache.

Download The Checklist (PDF, Apple Pages)

With this checklist in mind, you should be prepared for any kind of front-end performance project. Feel free to download the print-ready PDF of the checklist as well as an editable Apple Pages document to customize the checklist for your needs:

  • Download the checklist PDF (PDF, 166 KB)
  • Download the checklist in Apple Pages (.pages, 275 KB)
  • Download the checklist in MS Word (.docx, 151 KB)

If you need alternatives, you can also check the front-end checklist by Dan Rublic, the "Designer's Web Performance Checklist" by Jon Yablonski and the FrontendChecklist.

Lá vamos nós!

Some of the optimizations might be beyond the scope of your work or budget or might just be overkill given the legacy code you have to deal with. That's fine! Use this checklist as a general (and hopefully comprehensive) guide, and create your own list of issues that apply to your context. But most importantly, test and measure your own projects to identify issues before optimizing. Happy performance results in 2021, everyone!


A huge thanks to Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Barry Pollard, Patrick Hamann, Gideon Pyzer, Andy Davies, Maria Prosvernina, Tim Kadlec, Rey Bango, Matthias Ott, Peter Bowyer, Phil Walton, Mariana Peralta, Pepijn Senders, Mark Nottingham, Jean Pierre Vincent, Philipp Tellis, Ryan Townsend, Ingrid Bergman, Mohamed Hussain SH, Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Adir Amsalem, Aleksey Kulikov and Rodney Rehm for reviewing this article, as well as our fantastic community which has shared techniques and lessons learned from its work in performance optimization for everybody to use. Você está realmente arrasando!