O que fazer quando o CSS bloqueia a análise de página?

Recentemente, conduzi uma auditoria em um site e me deparei com um padrão preload/polyfillque já vi em vários clientes. Hoje, o uso desse padrão anteriormente popular não é recomendado. No entanto, é útil considerá-lo para ilustrar a importância do uso cuidadoso do mecanismo de pré-carregamento de materiais pelos navegadores da web. Também é interessante porque permite demonstrar um exemplo real de como a ordem dos elementos em um documento pode afetar o desempenho (é o que é discutido neste maravilhoso artigo de Harry Roberts). O material, cuja tradução publicamos hoje, é dedicado à análise de situações nas quais o manuseio inadequado e prematuro dos recursos de CSS afeta o desempenho das páginas da web.





Sobre loadCSS


Sou um grande fã do Filament Group - eles lançam uma quantidade incrível de ótimos projetos. Além disso, eles constantemente criam ferramentas valiosas e as compartilham para melhorar a Web. Uma dessas ferramentas é o loadCSS , que por muito tempo foi a mesma ferramenta que eu recomendei que todos usassem para carregar recursos CSS não críticos.

Embora isso tenha mudado agora (e o Filament Group tenha publicado um excelente artigo sobre o que seus funcionários preferem usar atualmente), ainda assim, quando audito sites, frequentemente vejo como eles o loadCSSusam na produção.

Um dos padrões que me deparei é o padrãopreload/polyfill. Usando essa abordagem, todos os arquivos com estilos são carregados no modo de pré-carregamento (o atributo dos rellinks correspondentes é definido como preload). Depois disso, quando estiverem prontos para uso, aplique seus eventos onloadpara conectá-los à página.

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>

Como nem todos os navegadores suportam o design <a href="https://caniuse.com/#feat=link-rel-preload"><link rel="preload"></a>, o projeto loadCSSfornece aos desenvolvedores um polyfill conveniente, que é adicionado à página após a descrição dos links relevantes:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

Desordem nas prioridades da rede


Eu nunca fui um fã ardente desse padrão. O pré-carregamento é uma ferramenta rude. Os materiais baixados usando links de atributo são rel="preload"bem-sucedidos com outros materiais para recursos de rede. O uso preloadpressupõe que as folhas de estilo carregadas de forma assíncrona devido ao fato de não desempenharem um papel crítico na saída de uma página recebam uma prioridade muito alta dos navegadores.

A imagem a seguir, tirada do WebPageTest, demonstra esse problema muito bem. Nas linhas 3-6, você pode ver o carregamento assíncrono de arquivos CSS usando um padrão preload. Mas, embora os desenvolvedores considerem esses arquivos não tão importantes que o download deles impediria a renderização, usepreload significa que eles serão carregados antes que o navegador receba os recursos restantes.


Os arquivos CSS que usam o padrão de pré-carregamento quando carregados chegam ao navegador mais cedo do que outros recursos, mesmo que não sejam extremamente necessários para a renderização inicial da página

Bloqueio do analisador HTML


Os problemas associados à prioridade do carregamento de recursos já são suficientes para evitar o uso do padrão na maioria das situações preload. Mas, neste caso, a situação é agravada pela presença de outra folha de estilo, carregada da maneira usual.

<link rel="stylesheet" href="path/to/main.css" />
<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

Os mesmos problemas ao usar os preloadleads levam ao fato de que os arquivos mais importantes não têm alta prioridade. Mas é tão importante, e talvez menos óbvio, que efeito isso tem na capacidade do navegador de analisar a página.

Novamente, isso já foi escrito em detalhes , por isso recomendo a leitura desse material para entender melhor o que está acontecendo. Aqui vou falar sobre isso brevemente.

Normalmente, o carregamento de estilos bloqueia a renderização da página. O navegador precisa consultar e analisar estilos para poder exibir a página. No entanto, isso não impede que o navegador analise o restante do código HTML.

Os scripts, por outro lado, bloqueiam o analisador se não estiverem marcados como deferou async.

Como o navegador deve assumir que o script pode estar manipulando o conteúdo da própria página ou os estilos aplicados a ela, ele precisa ter cuidado com a execução desse script. Se o navegador souber que algum código CSS está carregando, ele aguardará a chegada desse código CSS e depois executará o script. E como o navegador não pode continuar analisando o documento até que o script seja executado, isso significa que os estilos não bloqueiam mais a renderização. Eles impedem o navegador de analisar o HTML.

Esse comportamento é verdadeiro para scripts externos e scripts incorporados na página. Se o CSS for carregado, os scripts embutidos não serão executados até que este CSS chegue ao navegador.

Estudo de problemas


A maneira mais compreensível de visualizar esse problema é usar as ferramentas de desenvolvedor do Chrome (eu realmente gosto do nível em que essas ferramentas cresceram).

Entre as ferramentas do Chrome, há uma guia Performancecom a qual você pode gravar o perfil de carregamento da página. Eu recomendo desacelerar artificialmente a conexão de rede para que o problema se manifeste mais claramente.

Nesse caso, testei usando a configuração de rede 3G rápida. Se você observar atentamente o que acontece com o encadeamento principal, poderá entender que a solicitação para carregar o arquivo CSS ocorre no início da análise de HTML (cerca de 1,7 segundos após o carregamento da página).


Um pequeno retângulo abaixo do bloco de análise HTML representa uma solicitação para receber um arquivo CSS

Durante o próximo período de tempo, que é aproximadamente um segundo, o encadeamento principal fica inativo. Aqui você pode ver pequenas ilhas de atividade. É um acionamento de eventos indicando a conclusão do carregamento de estilos; é o envio pelo mecanismo de pré-carregamento de recursos de outras solicitações. Mas o navegador para completamente de analisar o HTML.


Se você observar o cenário geral, após o início do carregamento do CSS, o encadeamento principal ficará inativo por mais de 1,1 segundos.Portanto

, 2,8 segundos se passaram, o estilo foi carregado e o navegador o processou. Somente então vemos o processamento do script interno e, depois disso, o navegador finalmente retorna à análise de HTML.


O CSS chega em cerca de 2,8 segundos, após o que vemos que o navegador continua analisando o HTML

Firefox é uma boa exceção


O comportamento acima é comum para Chrome, Edge e Safari. O Firefox é uma boa exceção à lista de navegadores populares.

Todos os outros navegadores suspendem a análise de HTML, mas usam um analisador proativo (um meio de pré-carregamento de materiais) para exibir o código dos links para recursos externos e para atender às solicitações de carregamento desses recursos. O Firefox, no entanto, dá um passo adiante neste assunto: constrói especulativamente uma árvore DOM, embora espere que o script seja executado.

A menos que o script manipule o DOM, o que leva à necessidade de descartar os resultados da análise especulativa, essa abordagem permite que o Firefox aproveite. Obviamente, se o navegador tiver que descartar a árvore DOM especulativamente construída, significa que, ao construir essa árvore, ela não serviu para nada.

Esta é uma abordagem interessante. Fiquei terrivelmente curioso para saber como é eficaz. Agora, no entanto, não há informações sobre isso no Firefox Performance Profiler. Não é possível descobrir se o analisador especulativo funcionou, se é necessário refazer o trabalho realizado e se ainda precisa ser refeito, como isso afetará o desempenho.

Conversei com os responsáveis ​​pelas ferramentas de desenvolvedor do Firefox e posso dizer que eles têm idéias interessantes sobre como apresentar essas informações no criador de perfil no futuro. Espero que tenham sucesso.

Solução para o problema


No caso do cliente, que mencionei no início, o primeiro passo para resolver esse problema parecia extremamente simples: livrar-se do padrão preload/polyfill. O pré-carregamento de CSS não crítico não faz sentido. Aqui você precisa mudar para usar, em vez de rel="preload", um atributo media="print". É exatamente isso que os especialistas do Grupo de Filamentos recomendam. Além disso, essa abordagem permite que você se livre completamente do polyfill.

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

Isso já nos coloca em uma posição melhor: agora as prioridades da rede estão muito mais alinhadas com a real importância dos downloads. E também nos livramos do script embutido.

Nesse caso, ainda há outro script interno localizado no cabeçalho do documento, abaixo da linha que inicia a solicitação para carregar CSS. Se você mover esse script para que fique na frente da linha que carrega o CSS, isso eliminará o bloqueio do analisador. Se você analisar a página novamente usando as Ferramentas do desenvolvedor do Chrome, a diferença será completamente óbvia.


Antes de fazer alterações no código da página, o analisador HTML parou na linha 1939, tendo encontrado um script interno, e permaneceu aqui por cerca de um segundo. Após a otimização, ele conseguiu chegar à linha 5281

Anteriormente, o analisador parou na linha 1939 e aguardava o carregamento do CSS, mas agora atinge a linha 5281. Lá, no final da página, existe outro script interno que interrompe o analisador novamente.

Esta é uma solução rápida para o problema. Esta não é a opção que representa a solução final para o problema. Alterar a ordem dos elementos e se livrar do padrãopreload/polyfillé apenas o primeiro passo. Você pode resolver esse problema da melhor maneira, incorporando código CSS crítico em uma página, em vez de carregá-lo de um arquivo externo. padronizarpreload/polyfillProjetado para ser usado além do CSS embutido. Isso nos permite ignorar completamente os problemas associados aos scripts e garantir que o navegador, após executar a primeira solicitação, tenha todos os estilos necessários para renderizar a página.

Mas, por enquanto, deve-se observar que podemos obter um bom aumento de desempenho fazendo pequenas alterações no projeto, relacionadas à maneira como os estilos são carregados e à ordem dos elementos no DOM.

Sumário


  • Se você usar um loadCSSpadrão preload/polyfill, vá para o padrão de carregamento de estilos print.
  • Se você tiver estilos externos carregados da maneira usual (ou seja, usando links regulares para arquivos desses estilos), mova todos os scripts internos que podem ser movidos acima dos links para carregar estilos.
  • Incorpore estilos críticos na página para garantir o início da renderização mais rápido possível.

Queridos leitores! Você encontrou problemas para diminuir a renderização da página devido ao CSS?

O PS
RUVDS parabeniza todos os profissionais de TI em 8 de março!
Este ano, decidimos não dar tulipas e não fazer uma seleção de presentes nerd. Seguimos um caminho diferente e criamos a página IT is female para mostrar a presença de especialistas femininas em TI.


All Articles