Refatorando CSS: otimizando tamanho e desempenho (Parte 3)

Publicados: 2022-03-10
Resumo rápido ↬ A base de código refatorada deve resultar em desempenho semelhante ou aprimorado e integridade da base de código aprimorada. Afinal, se a implantação da base de código refatorada causar problemas de carregamento ou desempenho, isso resultará em menos tráfego e receita. Felizmente, existem muitas técnicas de otimização que podemos aplicar para lidar com possíveis problemas de tamanho de arquivo e desempenho.

Nos artigos anteriores desta série, abordamos a auditoria da integridade da base de código CSS e a estratégia, teste e manutenção de refatoração de CSS incremental. Independentemente de quanto a base de código CSS foi aprimorada durante o processo de refatoração e quanto mais sustentável e extensível ela é, a folha de estilo final precisa ser otimizada para o melhor desempenho possível e o menor tamanho de arquivo possível.

A implantação da base de código refatorada não deve resultar em pior desempenho do site e pior experiência do usuário. Afinal, os usuários não vão esperar para sempre pelo carregamento do site. Além disso, o gerenciamento ficará insatisfeito com a diminuição do tráfego e da receita causada pela base de código não otimizada, apesar das melhorias na qualidade do código.

Neste artigo, abordaremos estratégias de otimização de CSS que podem otimizar o tamanho do arquivo CSS, os tempos de carregamento e o desempenho de renderização. Dessa forma, a base de código CSS refatorada não é apenas mais fácil de manter e extensível, mas também de desempenho e verifica todas as caixas que são importantes tanto para o usuário final quanto para o gerenciamento.

Parte de: Refatoração CSS

  • Parte 1: Refatoração CSS: Introdução
  • Parte 2: Estratégia CSS, Teste de Regressão e Manutenção
  • Parte 3: Otimizando Tamanho e Desempenho
  • Assine nossa newsletter por e-mail para não perder as próximas.

Otimizando o tamanho do arquivo da folha de estilo

A otimização do tamanho do arquivo se resume a remover caracteres desnecessários e formatar e otimizar o código CSS para usar diferentes sintaxes ou propriedades abreviadas para reduzir o número total de caracteres em um arquivo.

Otimização e Minificação

A otimização e minificação de CSS existem há anos e se tornaram um elemento básico na otimização de front-end. Ferramentas como cssnano e clean-css estão entre minhas ferramentas favoritas quando se trata de otimização e minificação de CSS. Eles oferecem uma ampla variedade de opções de personalização para controlar ainda mais como o código está sendo otimizado e quais navegadores são suportados.

Essas ferramentas funcionam de maneira semelhante. Primeiro, o código não otimizado é analisado e transpilado seguindo as regras definidas no arquivo config. O resultado é o código que usa menos caracteres, mas ainda mantém a formatação (quebras de linha e espaços em branco).

 /* Before - original and unoptimized code */ .container { padding: 24px 16px 24px 16px; background: #222222; } /* After - optimized code with formatting */ .container { padding: 24px 16px; background: #222; }

E, finalmente, o código otimizado transpilado é reduzido pela remoção de toda a formatação de texto desnecessária . Dependendo da base de código e dos navegadores suportados definidos na configuração, o código com prefixos de fornecedores obsoletos também pode ser removido.

 /* Before - optimized code with formatting */ .container { padding: 24px 16px; background: #222; } /* After - optimized and minified code */ .container{padding:24px 16px;background:#222}

Mesmo neste exemplo básico, conseguimos reduzir o tamanho geral do arquivo de 76 bytes para 55 bytes, resultando em uma redução de 23%. Dependendo da base de código e das ferramentas de otimização e configuração, a otimização e minificação de CSS podem ser ainda mais eficazes.

A otimização e minificação de CSS pode ser considerada uma vitória fácil devido ao retorno significativo com apenas alguns ajustes no fluxo de trabalho de CSS. É por isso que a minificação deve ser tratada como a otimização de desempenho mínima e um requisito para todas as folhas de estilo do projeto.

Mais depois do salto! Continue lendo abaixo ↓

Otimizando consultas de mídia

Quando escrevemos consultas de mídia em CSS, especialmente ao usar vários arquivos (PostCSS ou Sass), geralmente não aninhamos o código em uma única consulta de mídia para um projeto inteiro. Para melhorar a capacidade de manutenção, modularidade e estrutura de código, geralmente escrevemos as mesmas expressões de consulta de mídia para vários componentes CSS.

Vamos considerar o seguinte exemplo de uma base de código CSS não otimizada.

 .page { display: grid; grid-gap: 16px; } @media (min-width: 768px) { .page { grid-template-columns: 268px auto; grid-gap: 24px; } } /* ... */ .products-grid { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 16px; } @media (min-width: 768px) { .products-grid { grid-template-columns: repeat(3, 1fr); grid-gap: 20px; } }

Como você pode ver, temos uma @media (min-width: 768px) por componente para melhor legibilidade e manutenção. Vamos executar a otimização e minificação neste exemplo de código e ver o que obtemos.

 .page{display:grid;grid-gap:16px}@media (min-width: 768px){.page{grid-template-columns:268px auto;grid-gap:24px}}.products-grid{display:grid;grid-template-columns:repeat(2,1fr);grid-gap:16px}@media (min-width: 768px){.products-grid{grid-template-columns:repeat(3,1fr);grid-gap:20px}}

Isso pode ser um pouco difícil de ler, mas tudo o que precisamos observar é a consulta de mídia repetida @media (min-width: 768px) . Já concluímos que queremos reduzir o número de caracteres em uma folha de estilo e podemos aninhar vários seletores em uma única consulta de mídia, então por que o minificador não removeu a expressão duplicada? Há uma razão simples para isso.

A ordem das regras é importante no CSS, portanto, para mesclar as consultas de mídia duplicadas, os blocos de código precisam ser movidos. Isso resultará na alteração da ordem das regras, o que pode causar efeitos colaterais indesejados nos estilos.

No entanto, a combinação de consultas de mídia pode tornar o tamanho do arquivo ainda menor, dependendo da base de código e da estrutura. Ferramentas e pacotes como postcss-sort-media-queries nos permitem remover consultas de mídia duplicadas e reduzir ainda mais o tamanho do arquivo.

Claro, há a importante ressalva de ter uma estrutura de base de código CSS bem estruturada que não depende da ordem das regras. Essa otimização deve ser levada em consideração ao planejar a refatoração de CSS e estabelecer regras básicas.

Eu recomendaria primeiro verificar se o benefício da otimização supera os riscos potenciais. Isso pode ser feito facilmente executando uma auditoria CSS e verificando as estatísticas de consulta de mídia. Se isso acontecer, eu recomendaria adicioná-lo mais tarde e executar testes de regressão automatizados para detectar quaisquer efeitos colaterais inesperados e bugs que possam ocorrer como resultado.

Removendo CSS não utilizado

Durante o processo de refatoração, sempre existe a possibilidade de você acabar com alguns estilos legados não utilizados que não foram completamente removidos ou você terá alguns estilos recém-adicionados que não estão sendo usados. Esses estilos também aumentam a contagem geral de caracteres e o tamanho do arquivo. Eliminar esses estilos não utilizados usando ferramentas automatizadas, no entanto, pode ser um pouco arriscado porque as ferramentas não podem prever com precisão quais estilos são realmente usados.

Ferramentas como purgecss percorrem todos os arquivos do projeto e usam todas as classes mencionadas nos arquivos como seletores, apenas para errar por precaução e não excluir acidentalmente seletores para elementos dinâmicos injetados em JavaScript, entre outros casos potenciais. No entanto, o purgecss oferece opções de configuração flexíveis como soluções alternativas para esses possíveis problemas e riscos.

No entanto, essa melhoria deve ser feita apenas quando os benefícios potenciais superam os riscos . Além disso, essa técnica de otimização exigirá um tempo considerável para configurar, configurar e testar, e pode causar problemas não intencionais no futuro, portanto, proceda com cautela e certifique-se de que a configuração seja à prova de balas.

Eliminando CSS de bloqueio de renderização

Por padrão, CSS é um recurso de bloqueio de renderização, o que significa que o site não será exibido para o usuário até que todas as folhas de estilo vinculadas e suas dependências (fontes, por exemplo) tenham sido baixadas e analisadas pelo navegador.

Exemplo de CSS de bloqueio de renderização com folha de estilo de fonte e dependência de arquivo de fonte
Exemplo de CSS de bloqueio de renderização com folha de estilo de fonte e dependência de arquivo de fonte. (De web.dev sob licença Creative Commons Attribution 4.0) (Visualização grande)

Se o arquivo de folha de estilo tiver um tamanho de arquivo grande ou várias dependências localizadas em servidores de terceiros ou CDNs, a renderização do site poderá ser atrasada significativamente, dependendo da velocidade e da confiabilidade da rede.

A maior pintura de conteúdo (LCP) tornou-se uma métrica importante nos últimos meses. O LCP não é importante apenas para o desempenho, mas também para o SEO – sites com melhores pontuações de LCP terão uma melhor classificação nos resultados de pesquisa. A remoção de recursos de bloqueio de renderização, como CSS, é uma maneira de melhorar a pontuação do LCP.

No entanto, se adiarmos o carregamento e o processamento da folha de estilo, isso resultaria em Flash Of Unstyled Content (FOUC) — o conteúdo seria exibido ao usuário imediatamente e os estilos seriam carregados e aplicados alguns momentos depois. Essa opção pode parecer chocante e pode até confundir alguns usuários.

CSS crítico

Com Critical CSS, podemos garantir que o site carregue com a quantidade mínima de estilos que são garantidos para serem usados ​​na página quando ela é renderizada inicialmente. Dessa forma, podemos tornar o FOUC muito menos perceptível ou até eliminá-lo na maioria dos casos. Por exemplo, se a página inicial apresenta um componente de cabeçalho com navegação e um componente de herói localizado acima da dobra, isso significa que o CSS crítico conterá todos os estilos globais e de componentes necessários para esses componentes, enquanto estilos para outros componentes na página será adiado.

Este CSS é embutido em HTML sob uma tag de style , então os estilos são carregados e analisados ​​juntamente com o arquivo HTML. Embora isso resulte em um tamanho de arquivo HTML um pouco maior (que também deve ser reduzido), todos os outros CSS não críticos serão adiados e não serão carregados imediatamente e o site será renderizado mais rapidamente. Em suma, os benefícios superam o aumento do tamanho do arquivo HTML.

 <head> <style type="text/css"><!-- Minified Critical CSS markup --></style> </head>

Existem muitas ferramentas automatizadas e pacotes NPM, dependendo da sua configuração, que podem extrair CSS crítico e gerar folhas de estilo adiadas.

Adiando folhas de estilo

Como exatamente fazemos com que o CSS não seja bloqueante? Sabemos que não deve ser referenciado no elemento HTML head quando a página HTML é baixada pela primeira vez. Demian Renzulli delineou esse método em seu artigo.

Não há uma abordagem HTML nativa (ainda) para otimizar ou adiar o carregamento de recursos de bloqueio de renderização, portanto, precisamos usar JavaScript para inserir a folha de estilo não crítica na marcação HTML após a renderização inicial. Também precisamos garantir que esses estilos sejam carregados de maneira não ideal (bloqueio de renderização) se um usuário estiver visitando a página com JavaScript não ativado no navegador.

 <!-- Deferred stylesheet --> <link rel="preload" as="style" href="path/to/stylesheet.css" onload="this.onload=null;this.rel='stylesheet'"> <!-- Fallback --> <noscript> <link rel="stylesheet" href="path/to/stylesheet.css"> </noscript>

Com o link rel="preload" as="style" garante que o arquivo de folha de estilo seja solicitado de forma assíncrona, enquanto o manipulador onload JavaScript garante que o arquivo seja carregado e processado pelo navegador após o carregamento do documento HTML. Alguma limpeza é necessária, portanto, precisamos definir o onload como null para evitar que essa função seja executada várias vezes e cause re-renderizações desnecessárias.

É exatamente assim que a Smashing Magazine lida com suas folhas de estilo. Cada modelo (página inicial, categorias de artigos, páginas de artigos, etc.) tem um CSS crítico específico do modelo embutido na tag de style HTML no elemento head e uma folha de estilo main.css adiada que contém todos os estilos não críticos.

No entanto, em vez de alternar o parâmetro rel , aqui podemos ver a consulta de mídia sendo alternada da mídia de print de baixa prioridade automaticamente adiada para o atributo de alta prioridade all quando a página terminar de carregar. Essa é uma abordagem alternativa e igualmente viável para adiar o carregamento de folhas de estilo não críticas.

 <link href="/css/main.css" media="print" onload="this.media='all'" rel="stylesheet">

Dividindo e carregando condicionalmente folhas de estilo com consultas de mídia

Para os casos em que o arquivo de folha de estilo final tem um tamanho de arquivo grande, mesmo após as otimizações mencionadas terem sido aplicadas, você pode dividir as folhas de estilo em vários arquivos com base em consultas de mídia e usar a propriedade de mídia nas folhas de estilo referenciadas no elemento HTML link para carregá-las condicionalmente .

 <link href="print.css" rel="stylesheet" media="print"> <link href="mobile.css" rel="stylesheet" media="all"> <link href="tablet.css" rel="stylesheet" media="screen and (min-width: 768px)"> <link href="desktop.css" rel="stylesheet" media="screen and (min-width: 1366px)">

Dessa forma, se uma abordagem mobile-first for usada, os estilos para tamanhos de tela maiores não serão baixados ou analisados ​​em dispositivos móveis que podem estar sendo executados em redes mais lentas ou não confiáveis.

Apenas para reiterar, este método deve ser usado se o resultado dos métodos de otimização mencionados anteriormente resultar em uma folha de estilo com tamanho de arquivo abaixo do ideal. Para casos normais, esse método de otimização não será tão eficaz ou impactante, dependendo do tamanho da folha de estilo individual.

Adiando arquivos de fonte e folhas de estilo

Adiar folhas de estilo de fonte (arquivos Google Font, por exemplo) também pode ser benéfico para o desempenho inicial da renderização. Concluímos que as folhas de estilo bloqueiam a renderização, mas também os arquivos de fonte referenciados na folha de estilo. Os arquivos de fonte também adicionam um pouco de sobrecarga ao desempenho inicial da renderização.

Carregar folhas de estilo de fonte e arquivos de fonte é um tópico complexo e mergulhar nele exigiria um artigo totalmente novo apenas para explicar todas as abordagens viáveis. Felizmente, Zach Leatherman delineou muitas estratégias viáveis ​​neste incrível guia abrangente e resumiu os prós e contras de cada abordagem. Se você usa o Google Fonts, Harry Roberts delineou uma estratégia para o carregamento mais rápido do Google Fonts.

Se você decidir adiar as folhas de estilo de fonte, você acabará com o Flash of Unstyled Text (FOUT). A página será inicialmente renderizada com a fonte de fallback até que os arquivos de fonte adiados e as folhas de estilo tenham sido baixados e analisados, quando os novos estilos serão aplicados. Essa mudança pode ser muito perceptível e pode causar mudanças de layout e confundir os usuários, dependendo do caso individual.

Barry Pollard delineou algumas estratégias que podem nos ajudar a lidar com o FOUT e falou sobre o próximo recurso CSS de ajuste de tamanho, que fornecerá uma maneira mais fácil e nativa de lidar com o FOUT.

Otimizações do lado do servidor

Compressão HTTP

Além da redução e otimização do tamanho do arquivo, ativos estáticos como HTML, arquivos CSS, arquivos JavaScript, etc. Algoritmos de compactação HTTP como Gzip e Brotli podem ser usados ​​para reduzir adicionalmente o tamanho do arquivo baixado.

A compactação HTTP precisa ser configurada no servidor, que depende da pilha de tecnologia e da configuração. No entanto, os benefícios de desempenho podem variar e podem não ter tanto impacto quanto a minimização e otimização da folha de estilo padrão, pois os navegadores ainda descompactarão os arquivos compactados e terão que analisá-los.

Cache de folhas de estilo

O armazenamento em cache de arquivos estáticos é uma estratégia de otimização útil. Os navegadores ainda terão que baixar os arquivos estáticos do servidor no primeiro carregamento, mas uma vez armazenados em cache, serão carregados diretamente nas solicitações subsequentes, acelerando o processo de carregamento.

O armazenamento em cache pode ser controlado por meio do cabeçalho HTTP Cache-Control no nível do servidor (por exemplo, usando o arquivo .htaccess em um servidor Apache).

Com max-age podemos indicar quanto tempo o arquivo deve ficar armazenado em cache (em segundos) no navegador e com public , estamos indicando que o arquivo pode ser armazenado em cache pelo navegador e quaisquer outros caches.

 Cache-Control: public, max-age=604800

Uma estratégia de cache mais agressiva e eficaz para ativos estáticos pode ser alcançada com a configuração immutable . Isso informa ao navegador que esse arquivo específico nunca será alterado e que quaisquer novas atualizações resultarão na exclusão desse arquivo e um novo arquivo com um nome de arquivo diferente tomará seu lugar. Isso é conhecido como cache-busting .

 Cache-Control: public, max-age=604800, immutable

Sem uma estratégia de bloqueio de cache adequada , existe o risco de perder o controle sobre os arquivos armazenados em cache no navegador do usuário. Isso significa que, se o arquivo for alterado, o navegador não poderá saber que deve baixar o arquivo atualizado e não usar o arquivo em cache desatualizado. E a partir desse ponto, não há praticamente nada que possamos fazer para corrigir isso e o usuário ficará preso ao arquivo desatualizado até que ele expire.

Para folhas de estilo, isso pode significar que, se atualizarmos arquivos HTML com novo conteúdo e componentes que exigem novo estilo, esses estilos não serão exibidos porque a folha de estilo desatualizada é armazenada em cache sem uma estratégia de bloqueio de cache e o navegador não saberá disso ele tem que baixar o novo arquivo.

Antes de usar uma estratégia de cache para folhas de estilo ou quaisquer outros arquivos estáticos, mecanismos eficazes de bloqueio de cache devem ser implementados para evitar que arquivos estáticos desatualizados fiquem presos no cache do usuário. Você pode usar um dos seguintes mecanismos de controle de versão para impedir o cache:

  • Anexando uma string de consulta ao nome do arquivo.
    Por exemplo styles.css?v=1.0.1. No entanto, alguns CDNs podem ignorar completamente ou remover a string de consulta do nome do arquivo e fazer com que o arquivo fique preso no cache do usuário e nunca seja atualizado.
  • Alterar o nome do arquivo ou anexar um hash.
    Por exemplo styles.a1bc2.css ou styles.v1.0.1.css. Isso é mais confiável e eficaz do que anexar uma string de consulta ao nome do arquivo.

CDN ou auto-hospedagem?

Content Delivery Network (CDN) é um grupo de servidores distribuídos geograficamente que são comumente usados ​​para a entrega confiável e rápida de ativos estáticos como imagens, vídeos, arquivos HTML, arquivos CSS, arquivos JavaScript, etc.

Embora os CDNs possam parecer uma ótima alternativa para ativos estáticos de auto-hospedagem, Harry Roberts fez uma pesquisa aprofundada sobre o assunto e concluiu que os ativos de auto-hospedagem são mais benéficos para o desempenho.

“Realmente há muito pouca razão para deixar seus ativos estáticos na infraestrutura de outra pessoa. Os benefícios percebidos geralmente são um mito e, mesmo que não fossem, as compensações simplesmente não valem a pena. O carregamento de ativos de várias origens é comprovadamente mais lento.”

Dito isto, eu recomendaria auto-hospedar as folhas de estilo (folhas de estilo de fonte incluídas, se possível) por padrão e mudar para CDN somente se houver razões viáveis ​​ou outros benefícios para fazê-lo.

Auditoria de tamanho e desempenho do arquivo CSS

WebPageTest e outras ferramentas de auditoria de desempenho semelhantes podem ser usadas para obter uma visão geral detalhada do processo de carregamento do site, tamanhos de arquivo, recursos de bloqueio de renderização etc. Essas ferramentas podem fornecer uma visão de como seu site é carregado em uma ampla variedade de dispositivos — desde um PC desktop rodando em uma rede de alta velocidade até smartphones de baixo custo rodando em redes lentas e não confiáveis.

Vamos fazer uma auditoria de desempenho em um site mencionado no primeiro artigo desta série — aquele com 2 MB de CSS minificado.

Primeiro, examinaremos o detalhamento do conteúdo para determinar quais recursos ocupam mais largura de banda. Nos gráficos a seguir, podemos ver que as imagens atendem à maioria das solicitações, o que significa que elas precisam ser carregadas com preguiça. A partir do segundo gráfico, podemos ver que as folhas de estilo e os arquivos JavaScript são os maiores em termos de tamanho de arquivo. Essa é uma boa indicação de que esses arquivos precisam ser reduzidos e otimizados, refatorados ou divididos em vários arquivos e carregados de forma assíncrona.

Dois gráficos mostrando a divisão do conteúdo por tipo MIME
Detalhamento de conteúdo por tipo MIME (na primeira visualização). (Visualização grande)

Podemos tirar ainda mais conclusões dos gráficos do Web Vitals. Ao dar uma olhada no gráfico Largest Contentful Paint (LCP), podemos obter uma visão geral detalhada dos recursos de bloqueio de renderização e o quanto eles afetam a renderização inicial.

Já pudemos concluir que a folha de estilo do site terá o maior impacto no LCP e nas estatísticas de carregamento. No entanto, podemos ver folhas de estilo de fonte, arquivos JavaScript e imagens referenciadas dentro das folhas de estilo que também bloqueiam a renderização. Sabendo que podemos aplicar os métodos de otimização mencionados acima para reduzir o tempo de LCP eliminando recursos de bloqueio de renderização.

o maior gráfico de pintura de conteúdo
Um gráfico para a maior pintura de conteúdo que acontece em 8561ms. Observe a lâmpada laranja na linha do tempo na lista de recursos — esses recursos estão bloqueando a renderização. (Visualização grande)

Conclusão

O processo de refatoração não está completo quando a integridade e a qualidade do código foram aprimoradas e quando os pontos fracos e os problemas da base de código foram corrigidos. A base de código refatorada deve resultar no mesmo desempenho ou melhor em comparação com a base de código legada.

Os usuários finais não devem enfrentar problemas de desempenho ou longos tempos de carregamento da base de código refatorada. Felizmente, existem muitos métodos para garantir que as bases de código sejam robustas e de alto desempenho - desde os métodos simples de minificação e otimização até métodos mais complexos, como eliminar recursos de bloqueio de renderização e divisão de código.

Podemos usar várias ferramentas de auditoria de desempenho, como WebPageTest, para obter uma visão geral detalhada dos tempos de carregamento, desempenho, recursos de bloqueio de renderização e outros fatores para que possamos resolver esses problemas com antecedência e eficácia.

Parte de: Refatoração CSS

  • Parte 1: Refatoração CSS: Introdução
  • Parte 2: Refatoração de CSS: Estratégia, Teste de Regressão e Manutenção
  • Parte 3: Refatoração CSS: Otimizando tamanho e desempenho
  • Assine nossa newsletter por e-mail para não perder as próximas.

Referências

  • “CSS de bloqueio de renderização”, Ilya Grigorik
  • “Defer Non-Critical CSS,” Demian Renzulli
  • “Um guia abrangente para estratégias de carregamento de fontes”, Zach Leatherman
  • “Uma nova maneira de reduzir o impacto do carregamento de fontes: descritores de fontes CSS”, Barry Pollard
  • “Auto-hospede seus ativos estáticos”, Harry Roberts
  • “Otimize o carregamento e a renderização de WebFont”, Ilya Grigorik