Desenvolvimento

19 mai, 2014

Desempenho e elementos personalizados

Publicidade

No fim do ano passado, eu me aprofundei no desempenho de elementos personalizados e encontrei algumas surpresas.

No meu artigo anterior, Anúncios assíncronos com imports do HTML, eu reclamei que os imports do HTML bloqueiam a renderização da página quando uma tag SCRIPT é encontrada e lamentei o fato de que a especificação não fornece um mecanismo para fazer esse acoplamento assíncrono. Elementos personalizados são exatamente o oposto: eles são assíncronos por padrão, e a especificação não fornece uma maneira de torná-los síncronos.

Preparando e consumindo

É importante entender por que precisamos de ambos os mecanismos síncronos e assíncronos para o carregamento de conteúdo.

  • Às vezes, o conteúdo é tão crucial para a página que deve ser processado antes de qualquer outra coisa. Nessas situações, é necessário para carregar o conteúdo de forma síncrona a fim de alcançar a experiência de usuário desejada pelo dono do site e designers. Carregamento síncrono também é necessário em outras situações, como quando existem dependências entre os recursos (por exemplo, scripts interdependentes) e para evitar mudanças gritantes no layout da página (também conhecidas como flash de conteúdo sem estilo ou FOUC, na sigla em inglês para Flash of Unstyled Content).
  • Outras vezes, o conteúdo vindo de subelementos da página é secundário em relação ao conteúdo da página principal, e os desenvolvedores e designers preferem carregá-lo de forma assíncrona. Esse é um padrão mais recente em desenvolvimento web. Carregar esses recursos menos críticos de forma assíncrona produz uma melhor experiência de usuário em termos de renderização e carregamento mais rápidos.

A questão fundamental é que há situações que exigem ambos os comportamentos, e os desenvolvedores precisam de uma forma de alcançar a experiência de usuário que consideram adequada. O principal papel de especificações e navegadores é fornecer ambos os mecanismos e escolher um bom padrão. Nós não fizemos isso nas versões anteriores do HTML, e estão tentando preencher essa lacuna agora com a especificação Resource Priorities, que adiciona o atributo lazyload a várias tags, incluindo IMG, SCRIPT, e LINK. Não queremos repetir esse processo de preencher-lacunas-após-a-necessidade no futuro, por isso precisamos fornecer capacidades síncronas e assíncronas para os elementos HTML5 que estão sendo especificados agora – e isso inclui Web Components.

Elementos personalizados – como fazer?

Note que neste momento o único navegador que eu encontrei que suporta Web Components é o Chrome Canary – sendo assim, você vai precisar instalá-lo para brincar com os exemplos. Eu liguei as seguintes bandeiras em chrome :/ /flags/: Experimental Web Platform features, Experimental JavaScript e HTML Imports.

A maneira de definir um elemento personalizado é em JavaScript. Aqui está o elemento personalizado usado em meus exemplos. Ele cria um novo elemento personalizado chamado x-foo:

var XFooProto = Object.create(HTMLElement.prototype);

XFooProto.createdCallback = function() {
    this.innerHTML = '<div id="imported-content" style="background: #E99; border: 2px; font-size: 2em; text-align: center; padding: 8px; height: 100px;">CUSTOM ELEMENT</div>';
};

var XFoo = document.register('x-foo', {prototype: XFooProto});

Para tornar oselementos personalizados mais reutilizáveis, eles estão envoltos em um import do HTML:

<link rel="import" href="import-custom-element.php">

No documento HTML, podemos usar o elemento personalizado como qualquer outra tag HTML:

<x-foo></x-foo>

Desenvolvedores experientes reconhecem que isso cria uma condição de corrida: o que acontece se a tag x-foo for analisada antes de import-custom-element.php ser baixado?

Elementos personalizados (assíncronos) = FOUC

O primeiro exemplo, custom-element.php, demonstra a implementação típica de um elemento personalizado descrito acima. Se você carregá-lo (no Chrome Canary), verá que há um flash de conteúdo sem estilo (FOUC). Isso revela que os navegadores lidam com elementos personalizados de forma assíncrona: o import do HTML começa a baixar, mas o navegador continua a analisar a página. Quando atinge a tag x-foo, ele pula sobre ela como um elemento não reconhecido e renderiza o resto da página. Quando o import do HTML termina de carregar, o navegador preenche de volta o x-foo que faz com que o conteúdo da página salte para baixo – aproximadamente 100 pixels – uma experiência chocante de FOUC.

Isso é ótimo para processamento mais rápido! Eu amo que o padrão seja assíncrono. E certamente há situações em que isso não criaria FOUC (elementos personalizados que não são visíveis ou serão usados mais tarde) ou o FOUC não é tão chocante (abaixo da dobra, mudanças de estilo, mas não de layout). Mas em casos como este, em que o FOUC é indesejável, é preciso haver uma maneira de evitar essas mudanças disruptivas no layout. Infelizmente, a especificação não fornece uma maneira de fazer isso. Vejamos duas soluções possíveis.

Tamanho de elementos personalizados

A mudança dissonante no layout pode ser evitada se a página principal reservar espaço para o elemento personalizado. Isso é feito no exemplo custom-elemento-sized.php assim:

<div style="height: 120px;">
<x-foo></x-foo>
</div>

O elemento personalizado está dentro de um recipiente de tamanho fixo. Como mostra este exemplo, o conteúdo da página existente é processado imediatamente e quando o import do HTML finalmente termina de baixar, o elemento personalizado é preenchido sem uma mudança de layout. Conseguimos o melhor dos dois mundos!

A desvantagem dessa abordagem é que ela só funciona para os elementos personalizados que têm um tamanho fixo pré-definido. Essa condição pode funcionar para alguns elementos personalizados, mas certamente não para todos eles.

Elementos personalizados síncronos

O exemplo custom-elemento-sync.php mostra uma solução alternativa para evitar FOUC para elementos personalizados que têm um tamanho desconhecido. Infelizmente, essa técnica bloqueia a renderização para tudo que está abaixo do elemento personalizado na página. A solução é adicionar uma tag SCRIPT logo acima do elemento personalizado, por exemplo:

<script>
var foo=128;
</script>
<x-foo></x-foo>

Como mostrado no meu artigo anterior, os imports do HTML fazem com que o analisador pare na primeira tag SCRIPT que é encontrada. Há uma ligeira vantagem aqui, ao se ter certeza de que a única tag SCRIPT após o <link rel=”import”…> está logo antes do elemento personalizado – isso permite que o conteúdo acima do elemento personalizado seja renderizado sem bloqueios. Você pode ver isso em ação no exemplo – apenas o conteúdo abaixo do elemento personalizado é impedido de renderizar até que o HTML acabe a importação.

Ao bloquear tudo abaixo do elemento personalizado, nós já evitamos o problema FOUC, mas o custo é alto. O bloqueio dessa quantidade de conteúdo pode ser uma má experiência para o usuário de acordo com o conteúdo da página principal. Certamente, se o elemento personalizado ocupar toda a área acima da dobra (por exemplo, em um dispositivo móvel), então isso seria uma alternativa viável.

Seria melhor se a especificação para elementos personalizados incluísse uma maneira de torná-los síncronos. Uma solução proposta por Daniel Buchner e eu para a W3C Public Webapps é adicionar um atributo chamado “elements” aos importações do HTML:

<link rel=”import” href=”elements.html” elements=”x-carousel, x-button”>

O atributo “elements” é uma lista dos elementos personalizados que devem ser carregados de forma síncrona (em outras palavras, não é a lista de todos os elementos personalizados no import do HTML – apenas os que devem fazer com que a renderização seja bloqueada). Como o navegador faz o parser da página, ele iria “passar por cima” de todos os elementos personalizados, tal como acontece agora, a menos que ele encontrasse um elemento personalizado que está listado nos valores dos atributos “elements” (por exemplo, “carrossel-x” e ” botão-x”). Se um dos elementos personalizados listados é lido, o parser iria bloquear até que o elemento personalizado se tornasse definido ou todos os pedidos de import do HTML pendentes terminassem de carregar.

Cansado de hacks

Eu amo encontrar maneiras de fazer as coisas funcionarem do jeito que eu quero que elas funcionem, mas é errado recorrer a hacks para esses novos recursos do HTML5 para alcançar um comportamento básico como evitar FOUC e carregamento assíncrono. Felizmente, as especificações e implementações estão em estágios iniciais. Talvez ainda haja tempo para obter as mudanças. Uma parte importante disso é ouvir a comunidade de desenvolvimento web. Se você tem preferências e casos de uso para a importação de HTML e elementos personalizados , por favor, pesar dentro Um pouco de esforço hoje resultará em uma melhor Web amanhã .

Muito obrigado aos autores para estes artigos fantásticos em Web Components:

  • Web Components: Why You’re Already an Expert por Mark Dalgleish
  • HTML Imports #include for the web por Eric Bidelman
  • Custom Elements defining new elements in HTML por Eric Bidelman

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.stevesouders.com/blog/2013/11/26/performance-and-custom-elements/