Cada história de desempenho na web é semelhante, não é? Sempre começa com a tão esperada reforma do site. Um dia em que um projeto, totalmente polido e cuidadosamente otimizado, é lançado, tendo uma classificação alta e superando as pontuações de desempenho no Lighthouse e WebPageTest. Há uma celebração e um sentimento sincero de realização prevalecendo no ar-lindamente refletido em retuítes e comentários e boletins informativos e tópicos do Slack.
No entanto, com o passar do tempo, o entusiasmo desaparece lentamente e ajustes urgentes, recursos muito necessários e novos requisitos de negócios aparecem. E de repente, antes que você perceba, a base de código fica um pouco sobrecarregada e fragmentados , os scripts de terceiros precisam ser carregados um pouco mais cedo, e o novo conteúdo dinâmico encontra seu caminho para o DOM por meio dos backdoors de scripts de terceiros e de seus convidados indesejados.
Também estivemos presentes no Smashing. Poucas pessoas sabem disso, mas somos uma equipe muito pequena de cerca de 12 pessoas, muitas das quais trabalhando meio período e a maioria delas geralmente usando vários cargos diferentes em um determinado dia. Embora o desempenho tenha sido nosso objetivo há quase uma década , nós nunca tive uma equipe de desempenho dedicada.
Após a última reformulação no final de 2017, era Ilya Pukhalski no lado JavaScript das coisas (tempo parcial), Michael Riethmueller no lado CSS das coisas (algumas horas por semana) e, sinceramente, jogando jogos mentais com CSS crítico e tentando conciliar alguns também muitas coisas.
Acontece que perdemos a noção do desempenho na agitação da rotina do dia-a-dia. Estávamos projetando e construindo coisas, configurando novos produtos, refatorando os componentes e publicando artigos. Então, no final de 2020, as coisas ficaram um pouco fora de controle, com as pontuações do Farol vermelho-amarelado aparecendo lentamente no quadro. Tínhamos que consertar isso.
É onde estávamos
Alguns de vocês devem saber que estamos executando em JAMStack , com todos os artigos e páginas armazenados como arquivos Markdown, arquivos Sass compilados em CSS, JavaScript dividido em partes com Webpack e Hugo criando páginas estáticas que servimos diretamente de um Edge CDN. Em 2017, construímos todo o site com o Preact, mas mudamos para o React em 2019-e o usamos junto com algumas APIs para pesquisa, comentários, autenticação e checkout.
Todo o site é construído com aprimoramento progressivo em mente, o que significa que você, caro leitor, pode ler cada artigo do Smashing em sua totalidade, sem a necessidade de inicializar o aplicativo. Também não é muito surpreendente-no final, um artigo publicado não muda muito com o passar dos anos, enquanto peças dinâmicas, como autenticação de membros e checkout, precisam que o aplicativo seja executado.
A compilação completa para implantar cerca de 2.500 artigos ao vivo leva cerca de 6 minutos no momento. O processo de construção por si só se tornou uma besta ao longo do tempo também, com injeções de CSS críticas, divisão de código do Webpack, inserções dinâmicas de anúncios e painéis de recursos, (re) geração de RSS e eventual teste A/B no limite.
No início de 2020, começamos com a grande refatoração dos componentes de layout CSS. Nunca usamos CSS-in-JS ou componentes estilizados, mas sim um bom e velho sistema baseado em componentes de módulos Sass que seriam compilados em CSS. Em 2017, todo o layout foi construído com Flexbox e reconstruído com CSS Grid e CSS Custom Properties em meados de 2019. No entanto, algumas páginas necessitaram de tratamento especial devido a novos spots publicitários e novos painéis de produtos. Enquanto o layout estava funcionando, ele não estava funcionando muito bem e era muito difícil de manter.
Além disso, o cabeçalho com a navegação principal teve que mudar para acomodar mais itens que queríamos exibir dinamicamente. Além disso, queríamos refatorar alguns componentes usados com frequência usados no site, e o CSS usado ali também precisava de alguma revisão-a caixa de boletim informativo sendo o culpado mais notável. Começamos refatorando alguns componentes com CSS que prioriza o utilitário, mas nunca chegamos ao ponto de ser usado de forma consistente em todo o site.
O maior problema era o grande pacote JavaScript que-não surpreendentemente-estava bloqueando o thread principal por centenas de milissegundos. Um grande pacote de JavaScript pode parecer deslocado em uma revista que apenas publica artigos, mas, na verdade, há muitos scripts acontecendo nos bastidores.
Temos vários estados de componentes para clientes autenticados e não autenticados. Depois de fazer login, queremos mostrar todos os produtos com o preço final e, conforme você adiciona um livro ao carrinho, queremos manter um carrinho acessível com um toque em um botão-independentemente da página em que você está. A publicidade precisa chegar rapidamente, sem causar mudanças de layout , e o mesmo vale para os painéis de produtos nativos que destacam nossos produtos. Além de um service worker que armazena em cache todos os ativos estáticos e os fornece para visualizações repetidas, junto com versões em cache de artigos que um leitor já visitou.
Portanto, todo esse script tinha que acontecer em algum ponto e estava esgotando a experiência de leitura, embora o script estivesse chegando tarde. Francamente, estávamos trabalhando meticulosamente no site e nos novos componentes, sem ficar de olho no desempenho (e tínhamos algumas outras coisas para manter em mente em 2020). O ponto de viragem veio inesperadamente. Harry Roberts realizou sua (excelente) Web Performance Masterclass como um workshop on-line conosco e durante todo o durante todo o workshop, ele estava usando o Smashing como exemplo, destacando problemas que tínhamos e sugerindo soluções para esses problemas juntamente com ferramentas e diretrizes úteis.
Ao longo do workshop, tomei notas diligentemente e revisitei a base de código. No momento do workshop, nossas pontuações no Lighthouse eram 60–68 na página inicial e em torno de 40-60 nas páginas do artigo -e obviamente pior no celular. Assim que o workshop acabou, começamos a trabalhar.
Identificando os gargalos
Muitas vezes, tendemos a confiar em pontuações específicas para compreender o nosso desempenho, mas muitas vezes pontuações simples não fornecem uma imagem completa. Como David East eloquentemente observou em seu artigo, o desempenho na web não é um único valor; é uma distribuição. Mesmo que uma experiência na web seja pesada e totalmente otimizada em um desempenho geral, ela não pode ser apenas rápida. Pode ser rápido para alguns visitantes, mas no final das contas também será mais lento (ou lento) para alguns outros.
As razões para isso são inúmeras, mas a mais importante é a enorme diferença nas condições da rede e no hardware do dispositivo em todo o mundo. Na maioria das vezes, não podemos realmente influenciar essas coisas, então temos que garantir que nossa experiência as acomode.
Em essência, nosso trabalho é aumentar a proporção de experiências rápidas e diminuir a proporção de experiências lentas. Mas, para isso, precisamos ter uma imagem adequada do que a distribuição realmente é. Agora, ferramentas de análise e ferramentas de monitoramento de desempenho fornecerão esses dados quando necessário, mas analisamos especificamente CrUX , Relatório de experiência do usuário do Chrome. CrUX gera uma visão geral das distribuições de desempenho ao longo do tempo, com o tráfego coletado dos usuários do Chrome. Muitos desses dados estão relacionados ao Core Web Vitals, que o Google anunciou em 2020, e que também contribuem e são expostos no Lighthouse.
Observamos que, de maneira geral, nosso desempenho regrediu drasticamente ao longo do ano, com quedas específicas em agosto e setembro. Depois de ver esses gráficos, poderíamos olhar para trás em alguns dos PRs que colocamos ao vivo naquela época para estudar o que realmente aconteceu.
Não demorou muito para descobrir que, nessa época, lançamos uma nova barra de navegação ao vivo. Essa barra de navegação-usada em todas as páginas-dependia do JavaScript para exibir itens de navegação em um menu com um toque ou clique, mas a parte JavaScript dela estava na verdade agrupada no pacote app.js . Para melhorar o Time To Interactive, decidimos extrair o script de navegação do pacote e exibi-lo inline.
Na mesma época, mudamos de um arquivo CSS crítico (desatualizado) criado manualmente para um sistema automatizado que gerava CSS essencial para cada modelo-página inicial, artigo, página de produto, evento, quadro de empregos e assim por diante-e CSS crítico embutido durante o tempo de construção. No entanto, não percebemos o quanto o CSS crítico gerado automaticamente era mais pesado. Tivemos que explorá-lo com mais detalhes.
E também na mesma época, estávamos ajustando o carregamento de fontes da web , tentando empurrar as fontes da web de forma mais agressiva com dicas de recursos, como pré-carregamento. Isso parece ser contrariando nossos esforços de desempenho, pois as fontes da web estavam atrasando a renderização do conteúdo, sendo priorizadas em excesso ao lado do arquivo CSS completo.
Agora, um dos motivos comuns para a regressão é o alto custo do JavaScript, então também olhamos para Webpack Bundle Analyzer e Mapa de solicitação de Simon Hearne para obter uma imagem visual de nossas dependências de JavaScript. Parecia bastante saudável no início.
Algumas solicitações chegavam ao CDN, um serviço de consentimento de cookies Cookiebot, Google Analytics, além de nossos serviços internos para exibição de painéis de produtos e publicidade personalizada. Não parecia que havia muitos gargalos-até olharmos um pouco mais de perto.
No trabalho de desempenho, é comum observar o desempenho de algumas páginas críticas-provavelmente a página inicial e provavelmente algumas páginas de artigo/produto. No entanto, embora haja apenas uma página inicial, pode haver muitas páginas de vários produtos, então precisamos escolher aquelas que representem nosso público.
Na verdade, como estamos publicando alguns artigos com muitos códigos e design no SmashingMag, ao longo dos anos acumulamos literalmente milhares de artigos que continham GIFs pesados, trechos de código destacados por sintaxe, incorporações de CodePen, incorporação de vídeo/áudio e encadeamentos aninhados de comentários sem fim.
Quando reunidos, muitos deles estavam causando nada menos que uma explosão no tamanho do DOM junto com o trabalho de thread principal excessivo -tornando a experiência mais lenta em milhares de páginas. Sem mencionar que, com a publicidade em vigor, alguns elementos DOM foram injetados no final do ciclo de vida da página, causando uma cascata de recálculos e redesenhos de estilo-também tarefas caras que podem produzir tarefas longas.
Tudo isso não estava aparecendo no mapa que geramos para uma página de artigo bastante leve no gráfico acima. Então, escolhemos as páginas mais pesadas que tínhamos-a toda poderosa página inicial , o mais longo , aquele com muitos vídeos incorporados e o um com muitas incorporações de CodePen -e decidimos otimizá-los o máximo que pudemos. Afinal, se eles são rápidos, as páginas com um único CodePen incorporado também devem ser mais rápidas.
Com essas páginas em mente, o mapa parecia um pouco diferente. Observe a linha enorme indo para o Vimeo player e Vimeo CDN, com 78 solicitações provenientes de um artigo do Smashing.
Para estudar o impacto no tópico principal, analisamos profundamente o painel Desempenho no DevTools. Mais especificamente, procurávamos tarefas que durassem mais de 50 ms (destacadas com um retângulo vermelho no canto superior direito) e tarefas que contivessem estilos de recálculo (barra roxa). O primeiro indicaria uma execução cara de JavaScript, enquanto o último exporia invalidações de estilo causadas por injeções dinâmicas de conteúdo no DOM e CSS abaixo do ideal. Isso nos deu algumas dicas práticas de por onde começar. Por exemplo, descobrimos rapidamente que nosso carregamento de fontes da web tinha um custo significativo de repintura, enquanto os pedaços de JavaScript ainda eram pesados o suficiente para bloquear o thread principal.
Como linha de base, olhamos atentamente para Core Web Vitals , tentando garantir que estamos marcando bem em todos eles. Escolhemos nos concentrar especificamente em dispositivos móveis lentos-com 3G lento, RTT de 400 ms e velocidade de transferência de 400 kbps, apenas para ficarmos no lado pessimista das coisas. Não é surpresa, então, que o Lighthouse não estava muito feliz com nosso site, fornecendo pontuações em vermelho totalmente sólidas para os artigos mais pesados e reclamando incansavelmente sobre JavaScript não utilizado, CSS, imagens fora da tela e seus tamanhos.
Assim que tivermos alguns dados, poderemos nos concentrar na otimização das três páginas mais pesadas do artigo, com foco em CSS crítico (e não crítico), pacote de JavaScript, tarefas longas, carregamento de fonte da web, mudanças de layout e incorporação de terceiros. Mais tarde, também revisaríamos a base de código para remover o código legado e usar novos recursos modernos do navegador. Parecia que havia muito trabalho pela frente e, de fato, estávamos muito ocupados nos próximos meses.
Melhorando a ordem dos ativos no
Ironicamente, a primeira coisa que examinamos não estava intimamente relacionada a todas as tarefas que identificamos acima. No workshop de desempenho, Harry gastou uma quantidade considerável de tempo explicando a ordem dos ativos no
de cada página, enfatizando que entregar conteúdo crítico rapidamente significa ser muito estratégico e estar atento sobre como os ativos são solicitados no código-fonte.
Agora não deve ser uma grande revelação que CSS crítico é benéfico para o desempenho da web. No entanto, veio como uma surpresa quanta diferença a ordem de todos os outros ativos-dicas de recursos, pré-carregamento de fonte da web, scripts síncronos e assíncronos, CSS completo e metadados-tem.
Viramos todo o
de cabeça para baixo, colocando CSS crítico antes de todos os scripts assíncronos e todos os ativos pré-carregados, como fontes, imagens etc. dividimos os recursos aos quais estaremos pré-conectando ou pré-carregando por modelo e tipo de arquivo, de modo que imagens críticas, realce de sintaxe e incorporações de vídeo sejam solicitadas antecipadamente apenas para um determinado tipo de artigos e páginas.
Em geral, orquestramos cuidadosamente o pedido no
, reduzimos o número de ativos pré-carregados que competiam por largura de banda e nos concentramos em acertar o CSS crítico. Se quiser se aprofundar em algumas das considerações críticas com o pedido
, Harry as destaca no artigo CSS e desempenho da rede . Essa mudança por si só nos trouxe cerca de 3-4 pontos de pontuação do Farol em todo o quadro.
Mudança do CSS crítico automatizado de volta ao CSS crítico manual
Mover as tags
foi uma parte simples da história. Mais difícil era a geração e gerenciamento de arquivos CSS críticos . Em 2017, criamos manualmente CSS essencial para cada modelo, coletando todos os estilos necessários para renderizar os primeiros 1000 pixels de altura em todas as larguras de tela. Obviamente, essa era uma tarefa incômoda e pouco inspiradora, sem mencionar os problemas de manutenção para controlar uma família inteira de arquivos CSS críticos e um arquivo CSS completo.
Por isso, examinamos as opções de automatizar este processo como parte da rotina de construção. Na verdade, não havia falta de ferramentas disponíveis, então testamos algumas e decidimos fazer alguns. Conseguimos configurá-los e executá-los rapidamente. A saída parecia ser boa o suficiente para um processo automatizado, então, após alguns ajustes na configuração, nós o conectamos e colocamos em produção. Isso aconteceu entre julho e agosto do ano passado, o que é bem visualizado no pico e na queda de desempenho nos dados CrUX acima. Continuamos indo e voltando com a configuração, muitas vezes tendo problemas com coisas simples, como adicionar estilos específicos ou remover outros. Por exemplo. estilos de prompt de consentimento de cookie que não estão realmente incluídos em uma página, a menos que o script de cookie tenha sido inicializado.
Em outubro, introduzimos algumas mudanças importantes no layout do site e, ao examinar o CSS crítico, nos deparamos com exatamente os mesmos problemas mais uma vez-o resultado gerado foi bastante prolixo e não foi exatamente o que queríamos. Portanto, como um experimento no final de outubro, todos reunimos nossos pontos fortes para revisitar nossa abordagem crítica de CSS e estudar o quão menor seria um CSS crítico feito à mão . Respiramos fundo e passamos dias em torno da ferramenta de cobertura de código nas páginas principais. Nós agrupamos as regras CSS manualmente e removemos duplicatas e código legado em ambos os lugares-o CSS crítico e o CSS principal. Na verdade, foi uma limpeza muito necessária, pois muitos estilos que foram escritos em 2017-2018 se tornaram obsoletos com o passar dos anos.
Como resultado, acabamos com três arquivos CSS críticos feitos à mão e com mais três arquivos que estão atualmente em andamento:
- critical-homepage-manual.css (8,2 KB, Brotlified)
- critical-article-manual.css (8 KB, Brotlified)
- critical-articles-manual.css (6 KB, Brotlified)
- critical-books-manual.css ( trabalho a ser feito )
- critical-events-manual.css ( trabalho a ser feito )
- critical-job-board-manual.css ( trabalho a ser feito )
Os arquivos estão embutidos no cabeçalho de cada modelo e, no momento, estão duplicados no pacote CSS monolítico que contém tudo o que já foi usado (ou realmente não é mais usado) no site. No momento, estamos pensando em dividir o pacote CSS completo em alguns pacotes CSS, para que um leitor da revista não baixe estilos do quadro de empregos ou das páginas do livro, mas quando chegar a essas páginas obterá uma renderização rápida com CSS crítico e obtenha o resto do CSS para essa página de forma assíncrona-apenas nessa página.
É certo que os arquivos CSS críticos feitos à mão não eram muito menores: reduzimos o tamanho dos arquivos CSS críticos em cerca de 14% . No entanto, eles incluíram tudo que precisávamos na ordem certa, do início ao fim, sem duplicatas e estilos de substituição. Isso parecia ser um passo na direção certa e nos deu um impulso de farol de mais 3–4 pontos. Estávamos fazendo progresso.
Alterando o carregamento da fonte da web
Com a exibição de fontes
ao nosso alcance, o carregamento de fontes parece ser um problema no passado. Infelizmente, não está certo em nosso caso. Vocês, queridos leitores, parecem visitar uma série de artigos na Smashing Magazine. Você também retorna frequentemente ao site para ler um outro artigo-talvez algumas horas ou dias depois, ou talvez uma semana depois. Um dos problemas que tivemos com font-display
usado em todo o site foi que, para os leitores que alternavam muito entre os artigos, notamos muitos flashes entre a fonte substituta e a fonte da web (que não deveria normalmente acontece, pois as fontes seriam armazenadas em cache corretamente).
Essa não pareceu uma experiência de usuário decente, então analisamos as opções. No Smashing, estamos usando duas fontes principais -Mija para títulos e Elena para texto. Mija vem em dois pesos (Regular e Bold), enquanto Elena vem em três pesos (Regular, Itálico, Bold). Abandonamos o Bold Italic de Elena anos atrás, durante o redesenho, apenas porque o usamos em apenas algumas páginas. Nós subdividimos as outras fontes removendo caracteres não usados e intervalos Unicode.
Nossos artigos são em sua maioria definidos em texto, então descobrimos que na maioria das vezes no site a pintura com maior conteúdo é o primeiro parágrafo do texto de um artigo ou a foto do autor. Isso significa que precisamos ter um cuidado extra para garantir que o primeiro parágrafo apareça rapidamente em uma fonte substituta, ao mesmo tempo em que mudamos para a fonte da web com refluxo mínimo.
Dê uma olhada na experiência de carregamento inicial da página inicial (desacelerado três vezes):
O primeiro ocorreu devido a caros recálculos de layout causados pela mudança das fontes (de fonte substituta para fonte web), causando mais de 290ms de trabalho extra (em um laptop rápido e uma conexão rápida). Removendo o estágio um apenas do carregamento de fontes, fomos capazes de ganhar cerca de 80ms de volta. Não era bom o suficiente porque estava muito além do orçamento de 50 ms. Então, começamos a cavar mais fundo.
O principal motivo para os recálculos acontecerem foi meramente por causa das enormes diferenças entre as fontes alternativas e as fontes da web. Ao combinar a altura da linha e os tamanhos de fontes substitutas e fontes da web , conseguimos evitar muitas situações em que uma linha de texto quebrava em uma nova linha na fonte substituta, mas ficava ligeiramente menor e caber na linha anterior, causando uma grande mudança na geometria da página inteira e, consequentemente, mudanças massivas de layout. Brincamos com espaçamento entre letras
e espaçamento entre palavras
também, mas não produziu bons resultados.
Com essas mudanças, conseguimos cortar outros 50-80ms, mas não conseguimos reduzir para menos de 120ms sem exibir o conteúdo em uma fonte substituta e depois exibir o conteúdo na fonte da web. Obviamente, isso deve afetar maciçamente apenas os visitantes de primeira viagem, visto que as visualizações de página consequentes seriam renderizadas com as fontes recuperadas diretamente do cache do service worker, sem refluxos dispendiosos devido à troca de fontes.
A propósito, é muito importante notar que, em nosso caso, notamos que a maioria das Tarefas Longas não foram causadas por JavaScript massivo, mas sim por Recálculos de Layout e análise do CSS, que significava que precisávamos fazer um pouco de limpeza de CSS, especialmente observando as situações em que os estilos são substituídos. De alguma forma, era uma boa notícia porque não tínhamos que lidar muito com problemas complexos de JavaScript. No entanto, não foi fácil, pois ainda estamos limpando o CSS hoje mesmo. Conseguimos remover duas Tarefas Longas para sempre, mas ainda temos algumas pendentes e ainda temos um longo caminho a percorrer. Felizmente, na maioria das vezes, não ultrapassamos o limite mágico de 50 ms.
O problema muito maior era o pacote JavaScript que servíamos, ocupando o thread principal por impressionantes 580ms. A maior parte desse tempo foi gasta inicializando o app.js , que contém React, Redux, Lodash e um carregador de módulo Webpack. A única maneira de melhorar o desempenho com este monstro enorme era quebrá-lo em pedaços menores. Então, pensamos em fazer exatamente isso.
Com o Webpack, dividimos o pacote monolítico em blocos menores com divisão de código , cerca de 30 KB por bloco. Fizemos alguma limpeza package.json e atualização de versão para todas as dependências de produção, ajustamos a configuração do browserlistrc para lidar com as duas versões mais recentes do navegador, atualizamos para Webpack e Babel para as versões mais recentes, mudamos para Terser para minificação, e usei ES2017 (+ browserlistrc) como destino para a compilação do script.
Também usamos BabelEsmPlugin para gerar versões modernas de dependências existentes. Por fim, adicionamos links de pré-busca ao cabeçalho para todos os fragmentos de script necessários e refatoramos o service worker, migrando para o Workbox com Webpack ( workbox-webpack-plugin ).
Lembra-se de quando mudamos para a nova navegação em meados de 2020, apenas para ver uma grande penalidade de desempenho como resultado? A razão para isso era bastante simples. Enquanto no passado a navegação era apenas HTML puro estático e um pouco de CSS, com a nova navegação, precisávamos de um pouco de JavaScript para abrir e fechar o menu no celular e no desktop. Isso estava causando cliques de raiva quando você clicava no menu de navegação e nada acontecia e, é claro, tinha um custo de penalidade nas pontuações do Tempo para a interação no Lighthouse.
Removemos o script do pacote e o extraímos como um script separado . Além disso, fizemos a mesma coisa para outros scripts autônomos que raramente eram usados -para realce de sintaxe, tabelas, incorporações de vídeo e incorporações de código-e os removemos do pacote principal; em vez disso, nós os carregamos granularmente apenas quando necessário.
No entanto, o que não notamos por meses foi que, embora tenhamos removido o script de navegação do pacote, ele estava carregando depois de todo o pacote app.js avaliado, o que não estava ajudando muito o Time-To-Interactive (veja a imagem acima). Nós o corrigimos pré-carregando nav.js e adiando sua execução na ordem de aparecimento no DOM, e conseguimos salvar outros 100 ms apenas com essa operação. No final, com tudo no lugar, conseguimos levar a tarefa para cerca de 220 ms.
Conseguimos algumas melhorias, mas ainda temos um longo caminho a percorrer, com otimizações adicionais do React e do Webpack em nossa lista de tarefas. No momento, ainda temos três tarefas longas -troca de fonte (120 ms), execução de app.js (220 ms) e recálculos de estilo devido ao tamanho do CSS completo (140 ms) ) Para nós, significa limpar e quebrar o CSS monolítico a seguir.
Vale a pena mencionar que esses resultados são realmente os resultados do melhor cenário . Em uma determinada página de artigo, podemos ter um grande número de incorporações de código e de vídeo, junto com outros scripts de terceiros e extensões de navegador do cliente que exigiriam uma conversa separada.
Lidando com terceiros
Felizmente, a pegada de nossos scripts de terceiros (e o impacto dos scripts de terceiros indiretos) não foi grande desde o início. Mas quando esses scripts de terceiros se acumulavam, eles reduziriam o desempenho significativamente. Isso vale especialmente para scripts de incorporação de vídeo , mas também para destaque de sintaxe, scripts de publicidade, scripts de painéis promocionais e qualquer incorporação de iframe externo.
Obviamente, adiamos todos esses scripts para começarem a carregar após o evento DOMContentLoaded, mas uma vez que eles finalmente entram no palco, eles causam um pouco de trabalho no thread principal. Isso aparece especialmente nas páginas do artigo, que obviamente representam a grande maioria do conteúdo do site.
A primeira coisa que fizemos foi alocar espaço adequado para todos os ativos que estão sendo injetados no DOM após a renderização da página inicial. Significa largura
e altura
para todas as imagens de publicidade e o estilo dos trechos de código. Descobrimos que, como todos os scripts foram adiados, novos estilos estavam invalidando os estilos existentes, causando mudanças massivas de layout para cada trecho de código exibido. Corrigimos isso adicionando os estilos necessários ao CSS crítico nas páginas do artigo.
Reestabelecemos uma estratégia para otimizar imagens (de preferência AVIF ou WebP-trabalho ainda em andamento). Todas as imagens abaixo do limite de altura de 1000px são nativamente carregadas lentamente (com
), enquanto as que estão no topo são priorizadas (
). O mesmo vale para todas as incorporações de terceiros.
Substituímos algumas partes dinâmicas por suas contrapartes estáticas-por exemplo, enquanto uma nota sobre um artigo salvo para leitura offline aparecia dinamicamente depois que o artigo foi adicionado ao cache do service worker, agora aparece estaticamente, pois estamos, bem, um pouco otimistas e esperamos que aconteça em todos os navegadores modernos.
No momento da escrita, estamos preparando facades para incorporações de código e de vídeo também. Além disso, todas as imagens que estão fora da tela receberão o atributo decoding=async
, so the browser has a free reign over when and how it loads images offscreen, asynchronously and in parallel.
To ensure that our images always include width and height attributes, we’ve also modified Harry Roberts’ snippet and Tim Kadlec’s diagnostics CSS to highlight whenever an image isn’t served properly. It’s used in development and editing but obviously not in production.
One technique that we used frequently to track what exactly is happening as the page is being loaded, was slow-motion loading.
First, we’ve added a simple line of code to the diagnostics CSS, which provides a noticeable outline for all elements on the page.
* { outline: 3px solid red
}
Then we record a video of the page loaded on a slow and fast connection. Then we rewatch the video by slowing down the playback and moving back and forward to identify where massive layout shifts happen.
Here’s the recording of a page being loaded on a fast connection:
The reason for the poor score on mobile is clearly poor Time to Interactive and poor Total Blocking time due to the booting of the app and the size of the full CSS file. So there is still some work to be done there.
As for the next steps, we are currently looking into further reducing the size of the CSS, and specifically break it down into modules, similarly to JavaScript, loading some parts of the CSS (e.g. checkout or job board or books/eBooks) only when needed.
We also explore options of further bundling experimentation on mobile to reduce the performance impact of the app.js although it seems to be non-trivial at the moment. Finally, we’ll be looking into alternatives to our cookie prompt solution, rebuilding our containers with CSS clamp()
, replacing the padding-bottom ratio technique with aspect-ratio
and looking into serving as many images as possible in AVIF.
That’s It, Folks!
Hopefully, this little case-study will be useful to you, and perhaps there are one or two techniques that you might be able to apply to your project right away. In the end, performance is all about a sum of all the fine little details, that, when added up, make or break your customer’s experience.
While we are very committed to getting better at performance, we also work on improving accessibility and the content of the site. So if you spot anything that’s not quite right or anything that we could do to further improve Smashing Magazine, please let us know in the comments to this article.
Finally, if you’d like to stay updated on articles like this one, please subscribe to our email newsletter for friendly web tips, goodies, tools and articles, and a seasonal selection of Smashing cats.