Design responsivo é hoje nossa melhor resposta à variedade de diferentes tamanhos de tela nas quais conteúdo online é consumido: a resolução dos smartphnes varia muito – orientação no formato “paisagem” e interação “touch”, diferentes densidades de pixels, e assim por diante. Os media queries do CSS3 permitem que o navegador altere a apresentação da página baseado nos atributos acima e ainda em outros, sem que seja necessário que modifiquemos o HTML da página – coisa muito boa.
Entretanto, quando o assunto é desempenho, ainda temos muito “enrosco” para resolver no nosso design responsivo. Primeiro, apesar de podermos otimizar a apresentação no cliente, a maior parte dos sites não otimiza o seguinte aspecto: você pode estar visualizando uma versão móvel do site, mas é provável que esteja baixando os mesmos recursos que a versão desktop, o que não é vantajoso para os dispositivos móveis.
eCSSential: carregamento personalizado de CSS
Com desempenho em mente, Scott Jehl criou o eCSSential, que tem como objetivo personalizar o carregamento das qualidades do CSS.
eCSSential é um utilitário JavaScript que foi projetado para que os navegadores baixem arquivos de uma maneira mais rápida e responsiva do que eles fazem por padrão. Utilizar tags links separadas com parâmetros media para referenciar folhas de estilo com os pontos de carregamento desejados não evita que essas folhas de estilo sejam baixadas e bloqueiem a renderização da página, mesmo em ambientes onde elas não são aplicáveis ou não serão aplicadas.
Falando tecnicamente, ele é um pequeno pedaço de JavaScript que, ao ser inserido no cabeçalho da página, determina quais das suas folhas de estilo devem ser carregadas imediatamente e bloqueia a renderização (qualquer folha de estilo programada para que dispositivos móveis carreguem primeiro), quais folhas de estilo devem ser adiadas para carregamento assíncrono (qualquer folha de estilo que não tenha relação com o tamanho da tela, mas que podem ser aplicadas mais tarde, depois de conhecer o tamanho da tela) e quais folhas de estilo nunca devem ser carregadas (quaisquer folhas de estilo destinadas a telas com porte maior do que a do dispositivo em questão).
Parece ótimo, exceto que o eCSSential tenta resolver problemas que não existem e que ao fazê-lo apenas acrescenta trabalho adicional – é um antipadrão de desempenho (performance antipattern).
Mito nº 1: todas as folhas de estilo bloqueiam a renderização
O navegador deve esperar até que ele tenha baixado, efetuado o parser e aplicado os CSSs relevante antes de poder renderizar a página na tela. Não respeitar isso irá resultar em um “flash of unstyled content” (FOUC), o que criaria uma experiência pobre para o usuário. Contudo, motores de renderização de navegadores modernos são muito mais espertos do que muitos de nós podemos supor. Vamos dar uma olhada na implementação HTMLLinkElement do WebKit:
// simplified version, with relevant bits to this discussion
void HTMLLinkElement::process()
{
if (m_disabledState != Disabled
&& (m_relAttribute.m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css")))
&& document()->frame() && m_url.isValid()) {
bool mediaQueryMatches = true;
if (!m_media.isEmpty()) {
mediaQueryMatches = evaluator.eval(media.get());
}
// Don't hold up render tree construction and script execution on stylesheets
// that are not needed for the rendering at the moment.
bool blocking = mediaQueryMatches && !isAlternate();
addPendingSheet(blocking ? Blocking : NonBlocking);
// Load stylesheets that are not needed for the rendering immediately with low priority.
ResourceLoadPriority priority = blocking ? ResourceLoadPriorityUnresolved : ResourceLoadPriorityVeryLow;
document()->cachedResourceLoader()->requestCSSStyleSheet(request, charset, priority);
}
}
O código deve falar por si mesmo. Se a folha de estilo estiver marcada como disabled, nós não a baixamos. Em seguida, se o elemento de link fornecer um media query, então ele é avaliado imediatamente: se o media query avaliar como falso, então a folha é marcada como NonBlocking e uma prioridade bem baixa de download é atribuída a ela. Aqui está um cenário de exemplo:
<!-- blocking stylesheet, nothing renders until it is downloaded and parsed --> <link href="main.css" rel="stylesheet"> <!-- non-blocking, low download priority because of the evaluated media query --> <link href="i-want-a-monitor-of-this-size.css" rel="stylesheet" media="(min-width: 4000px)"> <!-- won't be downloaded at all, because it is marked as disabled --> <link href="noop.css" rel="stylesheet" disabled> <!-- print stylesheet is non-blocking --> <link href="noop.css" rel="stylesheet" media="print">
Dado o código acima, apenas o main.css irá bloquear a renderização da página. Media queries são seus amigos nas tags link, use-os. O único contra, conforme indica Scott, é que o navegador baixará todas as folhas de estilo habilitadas, mesmo que o seu dispositivo nunca seja maior do que os 4000 pixels de largura. Esta é uma área de possível melhora para o WebKit: avaliar quando uma restrição pode ser satisfeita totalmente em um determinado dispositivo.
Mito nº 2: CSS é sempre o caminho crítico
O Scott está certo, carregar CSS de maneira otimizada e priorizada é algo difícil. Todavia, você não será capaz de fazer algo melhor do que o navegador a partir de JavaScript executado no lado do cliente. No exemplo acima, marquei a folha de estilo media=”print” como non-blocking, mas como? Já sabemos metade da resposta: se o media query não combinar, marcaremos com non-blocking e daremos a ele baixa prioridade de download. Agora, vamos ver a implementação do HTMLPreloadScanner no WebKit:
void preload(Document* document, bool scanningBody, const KURL& baseURL)
{
CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader();
ResourceRequest request = document->completeURL(m_urlToLoad, baseURL);
if (m_tagName == linkTag && m_linkIsStyleSheet && m_linkMediaAttributeIsScreen)
cachedResourceLoader->preload(CachedResource::CSSStyleSheet, request, m_charset, scanningBody);
}
static bool linkMediaAttributeIsScreen(const String& attributeValue)
{
// Only preload screen media stylesheets. Used this way, the evaluator evaluates to true for any rules
// containing complex queries (full evaluation is possible but it requires a frame and a style selector
// which may be problematic here).
MediaQueryEvaluator mediaQueryEvaluator("screen");
return mediaQueryEvaluator.eval(mediaQueries.get());
}
O preload scanner traz apenas as folhas de estilo com screen como tipo de dispositivo, que é o tipo padrão. Portanto, tv, print e outras folhas de estilo que não forem do tipo screen não competirão com recursos do tipo blocking para renderizar a página. Além disso, infiltrar folhas de estilo através de JavaScript apenas atrapalharia o network predictor do Chrome e aspectos do cache DNS.
Redes móveis: é complicado
Já estabelecemos que o navegador é esperto o suficiente para adiar o carregamento de folhas de estilo que (a) não combinam com o media query padrão e (b) não combinam com o tipo de dispositivo. Todas as outras folhas de estilo recebem um PriorityVeryLow para permitir que outros recursos mais importantes, como o JavaScript, passem a frente na fila de download. Mas não poderíamos fazer melhor ao requisitar algumas folhas de estilo sob demanda?
De forma contraintuitiva, isso levaria a uma experiência piorada dado o alto custo do link em uma rede móvel. Primeiro, se o dispositivo esteve ocioso por alguns segundos, o rádio é desligado. Para enviar uma requisição, nós ligamos o rádio e esperamos (1-2s), depois disso realizamos o handshake TCP (mais a resolução DNS, se necessária), o que leva a mais 200-1000ms, e só então baixamos o conteúdo: depois de 2-4 segundos, temos a nossa folha de estilo. Você está disposto a esperar tanto tempo ao redimensionar a sua tela, ou carregar uma visualização de impressão?
Dado que o tamanho médio dos CSS é de 37kb, é muito melhor baixar todo o conteúdo CSS de uma vez logo no início, com a conexão estabelecida. Ao fazer dessa forma, você também ajudará o usuário a prolongar a bateria dele. Se há alguma otimização a ser feita aqui, é simplesmente concatenar e comprimir.
Superando o navegador
Um navegador moderno realiza uma complicada dança em cada carregamento de página: pré-solicitação e pré-conexão de DNS, carregamento especulativo de recursos, aplicação de padrões utilizados anteriormente, e muito mais. Ao mesmo tempo em que sempre há espaço para melhorias, é muito improvável que você vá superá-lo com uma biblioteca que roda no lado do cliente – deixe o navegador otimizar o carregamento de recursos, ele já é muito bom nisso, e crie tickets nos quais o comportamento possa ser melhorado.
Concatene e comprima o seu CSS, utilize media queries nas suas tags link e deixe o navegador fazer o trabalho. Dado que um site serve hoje uma média de 37kb de CSS, o que é menos de 5% do total de 1066Kb, as chances são de que você vá ter ganhos muito maiores em outras áreas. Otimizar imagens, que correspondem a mais de 60% do tamanho de um site, é um bom começo.
***
Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.igvita.com/2012/06/14/debunking-responsive-css-performance-myths/



