Desempenho na Web facilitado: edição do Google I/O 2018

No Google I/O 2018, apresentamos um resumo de ferramentas, bibliotecas e técnicas de otimização que facilitam a melhoria do desempenho na Web. Aqui, explicamos esses conceitos usando o app Oodles Theater. Também falamos sobre nossos experimentos com carregamento preditivo e a nova iniciativa Guess.js.

Ewa Gasperowicz

No último ano, trabalhamos bastante para descobrir como tornar a Web mais rápida e com melhor desempenho. Isso levou a novas ferramentas, abordagens e bibliotecas que gostaríamos de compartilhar com você neste artigo. Na primeira parte, vamos mostrar algumas técnicas de otimização que usamos na prática ao desenvolver o app Oodles Theater. Na segunda parte, vamos falar sobre nossos experimentos com carregamento preditivo e a nova iniciativa Guess.js.

A necessidade de performance

A Internet fica cada vez mais pesada a cada ano. Se verificarmos o estado da Web, veremos que uma página mediana em dispositivos móveis pesa cerca de 1,5 MB, sendo a maior parte disso JavaScript e imagens.

O tamanho crescente dos sites, junto com outros fatores, como latência de rede, limitações de CPU, padrões de bloqueio de renderização ou código de terceiros supérfluo, contribui para o complicado quebra-cabeça de desempenho.

A maioria dos usuários classifica a velocidade como o principal item na hierarquia de necessidades da UX. Isso não é muito surpreendente, porque não é possível fazer muita coisa até que uma página termine de carregar. Não é possível extrair valor da página nem admirar a estética dela.

Pirâmide de hierarquia de UX
Fig. 1. Qual é a importância da velocidade para os usuários? (Speed Matters, Vol. 3)

Sabemos que a performance é importante para os usuários, mas descobrir por onde começar a otimizar pode parecer um segredo. Felizmente, existem ferramentas que podem ajudar você.

Lighthouse: uma base para o fluxo de trabalho de performance

O Lighthouse faz parte do Chrome DevTools e permite que você faça uma auditoria do seu site, além de dar dicas sobre como melhorá-lo.

Recentemente, lançamos várias novas auditorias de desempenho que são muito úteis no fluxo de trabalho de desenvolvimento diário.

Novas auditorias do Lighthouse
Fig. 2. Novas auditorias do Lighthouse

Vamos ver como você pode aproveitar esses recursos em um exemplo prático: o app Oodles Theater. É um pequeno app da Web de demonstração, em que você pode testar alguns dos nossos Google Doodles interativos favoritos e até jogar um ou dois jogos.

Ao criar o app, queríamos garantir que ele tivesse o melhor desempenho possível. O ponto de partida para a otimização foi um relatório do Lighthouse.

Relatório do Lighthouse para o app Oodles
Fig. 3. Relatório do Lighthouse para o app Oodles

O desempenho inicial do nosso app, conforme mostrado no relatório do Lighthouse, foi terrível. Em uma rede 3G, o usuário precisava esperar 15 segundos para a First Meaningful Paint ou para o app se tornar interativo. O Lighthouse destacou vários problemas no nosso site, e a pontuação geral de desempenho de 23 refletiu exatamente isso.

A página pesava cerca de 3,4 MB. Precisávamos reduzir esse tamanho urgentemente.

Isso deu início ao nosso primeiro desafio de desempenho: encontrar coisas que podemos remover facilmente sem afetar a experiência geral.

Oportunidades de otimização de performance

Remover recursos desnecessários

Há algumas coisas óbvias que podem ser removidas com segurança: espaços em branco e comentários.

Ganhos com a minificação
Fig. 4. Minificar e compactar JavaScript e CSS

O Lighthouse destaca essa oportunidade na auditoria de CSS e JavaScript não minificados. Estávamos usando o webpack para nosso processo de build. Portanto, para conseguir a minificação, simplesmente usamos o plug-in Uglify JS.

A minificação é uma tarefa comum, então você deve conseguir encontrar uma solução pronta para qualquer processo de build que use.

Outra auditoria útil nesse espaço é Ativar a compactação de texto. Não há motivo para enviar arquivos não compactados, e a maioria das CDNs já oferece suporte para isso.

Estávamos usando o Firebase Hosting para hospedar nosso código, e o Firebase ativa a compactação gzip por padrão. Portanto, pela simples virtude de hospedar nosso código em uma CDN razoável, conseguimos isso sem custo financeiro.

Embora o gzip seja uma forma muito conhecida de compactação, outros mecanismos como Zopfli e Brotli também estão ganhando força. O Brotli é compatível com a maioria dos navegadores, e você pode usar um binário para pré-compactar seus recursos antes de enviá-los ao servidor.

Usar políticas de cache eficientes

A próxima etapa foi garantir que não enviamos recursos duas vezes se não for necessário.

A auditoria de política de cache ineficiente no Lighthouse nos ajudou a perceber que poderíamos otimizar nossas estratégias de armazenamento em cache para alcançar exatamente isso. Ao definir um cabeçalho de expiração de idade máxima no servidor, garantimos que, em uma visita repetida, o usuário possa reutilizar os recursos que já foram transferidos por download.

O ideal é armazenar em cache o maior número possível de recursos com segurança pelo maior período possível e fornecer tokens de validação para uma revalidação eficiente dos recursos atualizados.

Remover código não utilizado

Até agora, removemos as partes óbvias do download desnecessário, mas e as partes menos óbvias? Por exemplo, código não utilizado.

Cobertura de código no DevTools
Fig. 5. Verificar a cobertura de código

Às vezes, incluímos nos nossos apps códigos que não são realmente necessários. Isso acontece principalmente se você trabalha no app por um período mais longo, se a equipe ou as dependências mudam e, às vezes, uma biblioteca órfã é deixada para trás. Foi exatamente o que aconteceu com a gente.

No início, usávamos a biblioteca de componentes do Material Design para criar um protótipo rápido do app. Com o tempo, mudamos para uma aparência mais personalizada e esquecemos completamente dessa biblioteca. Felizmente, a verificação de cobertura de código nos ajudou a redescobri-lo no pacote.

É possível verificar as estatísticas de cobertura de código no DevTools, tanto para o ambiente de execução quanto para o tempo de carregamento do aplicativo. Você pode ver as duas grandes listras vermelhas na captura de tela abaixo: mais de 95% do nosso CSS não foi usado, além de uma grande quantidade de JavaScript.

O Lighthouse também identificou esse problema na auditoria de regras CSS não usadas. Ele mostrou uma economia potencial de mais de 400 kb. Então, voltamos ao nosso código e removemos as partes de JavaScript e CSS da biblioteca.

Se descartarmos o adaptador MVC, nossos estilos vão cair para 10 KB.
Fig. 6. Se descartarmos o adaptador MVC, nossos estilos vão cair para 10 KB.

Isso reduziu nosso pacote CSS em 20 vezes, o que é muito bom para um commit pequeno de duas linhas.

Claro que isso aumentou nossa pontuação de desempenho, e o Time to Interactive também melhorou muito.

No entanto, com mudanças como essa, não basta verificar apenas as métricas e pontuações. Remover o código real nunca é livre de riscos. Por isso, sempre fique de olho em possíveis regressões.

Nosso código não foi usado em 95% dos casos, mas ainda há esses 5% em algum lugar. Parece que um dos nossos componentes ainda estava usando os estilos dessa biblioteca: as setinhas no controle deslizante do doodle. Como era muito pequeno, pudemos incorporar manualmente esses estilos de volta aos botões.

Os botões foram corrompidos devido à falta de uma biblioteca
Fig. 7. Um componente ainda estava usando a biblioteca removida

Portanto, se você remover o código, verifique se tem um fluxo de trabalho de teste adequado para ajudar a evitar possíveis regressões visuais.

Evite payloads de rede enormes

Sabemos que recursos grandes podem deixar o carregamento de páginas da Web mais lento. Eles podem custar dinheiro aos usuários e ter um grande impacto nos planos de dados deles. Por isso, é muito importante ter isso em mente.

O Lighthouse conseguiu detectar que tínhamos um problema com alguns dos nossos payloads de rede usando a auditoria Payload de rede enorme.

Detectar payloads de rede enormes
Fig. 8. Detectar payloads de rede enormes

Aqui, vimos que tínhamos mais de 3 MB de código sendo enviado, o que é bastante, especialmente em dispositivos móveis.

No topo da lista, o Lighthouse destacou que tínhamos um pacote de fornecedor de JavaScript com 2 MB de código não compactado. Esse também é um problema destacado pelo webpack.

Como diz o ditado: a solicitação mais rápida é aquela que não é feita.

O ideal é medir o valor de cada recurso que você está disponibilizando para os usuários, medir o desempenho desses recursos e decidir se vale a pena enviá-los com a experiência inicial. Porque, às vezes, esses recursos podem ser adiados, carregados lentamente ou processados durante o tempo de inatividade.

No nosso caso, como estamos lidando com muitos pacotes de JavaScript, tivemos sorte porque a comunidade do JavaScript tem um conjunto avançado de ferramentas de auditoria de pacotes de JavaScript.

Auditoria de pacotes JavaScript
Fig. 9. Auditoria de pacotes JavaScript

Começamos com o analisador de pacotes do webpack, que nos informou que estávamos incluindo uma dependência chamada unicode, que tinha 1,6 MB de JavaScript analisado, ou seja, bastante.

Em seguida, acessamos nosso editor e, usando o plug-in Import Cost para Visual Studio Code, conseguimos visualizar o custo de cada módulo que estávamos importando. Isso nos permitiu descobrir qual componente estava incluindo o código que fazia referência a esse módulo.

Em seguida, mudamos para outra ferramenta, o BundlePhobia. Essa ferramenta permite que você insira o nome de qualquer pacote NPM e veja o tamanho estimado dele minificado e compactado com gzip. Encontramos uma boa alternativa para o módulo de slug que estávamos usando, que pesava apenas 2,2 kb, então fizemos a troca.

Isso teve um grande impacto na nossa performance. Entre essa mudança e a descoberta de outras oportunidades para reduzir o tamanho do pacote JavaScript, economizamos 2,1 MB de código.

Observamos melhorias de 65% no geral, considerando o tamanho compactado com gzip e minificado desses pacotes. E descobrimos que esse processo realmente valia a pena.

Portanto, em geral, tente eliminar downloads desnecessários nos seus sites e apps. Fazer um inventário dos seus recursos e medir o impacto deles na performance pode fazer uma grande diferença. Por isso, faça auditorias regulares.

Reduzir o tempo de inicialização do JavaScript com a divisão de código

Embora os payloads de rede grandes possam ter um grande impacto no app, há outro fator que pode ter um impacto realmente grande: o JavaScript.

O JavaScript é o recurso mais caro. Em dispositivos móveis, se você enviar grandes pacotes de JavaScript, isso pode atrasar a interação dos usuários com os componentes da interface. Isso significa que eles podem tocar na interface sem que nada de significativo aconteça. Por isso, é importante entender por que o JavaScript custa tanto.

É assim que um navegador processa o JavaScript.

Processamento de JavaScript
Fig. 10. Processamento de JavaScript

Primeiro, precisamos fazer o download desse script. Temos um mecanismo JavaScript que precisa analisar, compilar e executar esse código.

Essas fases não levam muito tempo em um dispositivo sofisticado, como um computador, um laptop ou até mesmo um smartphone sofisticado. Mas em um smartphone comum, esse processo pode levar de cinco a dez vezes mais tempo. Isso é o que atrasa a interatividade, então é importante tentar reduzir esse tempo.

Para ajudar você a descobrir esses problemas no seu app, introduzimos uma nova auditoria de tempo de inicialização do JavaScript no Lighthouse.

Tempo de inicialização do JavaScript
Fig. 11. Auditoria do tempo de inicialização do JavaScript

No caso do app Oodle, ele nos informou que o tempo gasto na inicialização do JavaScript foi de 1,8 segundo. O que estava acontecendo era que estávamos importando estaticamente todas as nossas rotas e componentes em um único pacote monolítico de JavaScript.

Uma técnica para contornar isso é usar a divisão de código.

A divisão de código é como pizza

A divisão de código é a noção de que, em vez de dar aos usuários uma pizza inteira de JavaScript, você dá apenas uma fatia de cada vez, conforme necessário.

A divisão de código pode ser aplicada no nível da rota ou do componente. Ele funciona muito bem com React e React Loadable, Vue.js, Angular, Polymer, Preact e várias outras bibliotecas.

Incorporamos a divisão de código ao nosso aplicativo e mudamos de importações estáticas para dinâmicas, o que nos permitiu carregar o código de forma assíncrona e lazy loading conforme necessário.

Divisão de código com importações dinâmicas
Fig. 13. Divisão de código com importações dinâmicas

O impacto disso foi a redução do tamanho dos nossos pacotes e do tempo de inicialização do JavaScript. O tempo caiu para 0,78 segundo, tornando o app 56% mais rápido.

Em geral, se você estiver criando uma experiência com muito JavaScript, envie apenas o código necessário ao usuário.

Aproveite conceitos como divisão de código, explore ideias como tree shaking e confira o repositório webpack-libs-optimizations para ter algumas ideias de como reduzir o tamanho da biblioteca se você estiver usando o webpack.

Otimizar imagens

Piada sobre o desempenho do carregamento de imagens

No app Oodle, usamos muitas imagens. Infelizmente, o Lighthouse não ficou tão entusiasmado quanto nós. Na verdade, falhamos nas três auditorias relacionadas a imagens.

Esquecemos de otimizar as imagens, não as dimensionamos corretamente e também poderíamos ter algum ganho usando outros formatos de imagem.

Auditorias de imagens
Fig. 14. Auditorias de imagens do Lighthouse

Começamos otimizando nossas imagens.

Para uma rodada de otimização única, use ferramentas visuais como ImageOptim ou XNConvert.

Uma abordagem mais automatizada é adicionar uma etapa de otimização de imagem ao processo de build, com bibliotecas como imagemin.

Assim, você garante que as imagens adicionadas no futuro sejam otimizadas automaticamente. Algumas CDNs, como a Akamai, ou soluções de terceiros, como Cloudinary, Fastly ou Uploadcare, oferecem soluções abrangentes de otimização de imagens. Assim, você também pode simplesmente hospedar suas imagens nesses serviços.

Se você não quiser fazer isso devido ao custo ou a problemas de latência, projetos como Thumbor ou Imageflow oferecem alternativas auto-hospedadas.

Antes e depois da otimização
Fig. 15. Antes e depois da otimização

O PNG de plano de fundo foi sinalizado no webpack como grande, e com razão. Depois de dimensioná-lo corretamente para a janela de visualização e executá-lo no ImageOptim, reduzimos para 100 kb, o que é aceitável.

Repetir isso para várias imagens no site permitiu reduzir significativamente o peso geral da página.

Usar o formato certo para conteúdo animado

Os GIFs podem ser muito caros. Surpreendentemente, o formato GIF nunca foi criado para ser uma plataforma de animação. Portanto, mudar para um formato de vídeo mais adequado oferece uma grande economia em termos de tamanho de arquivo.

No app Oodle, usávamos um GIF como sequência de introdução na página inicial. De acordo com o Lighthouse, poderíamos economizar mais de 7 MB se mudássemos para um formato de vídeo mais eficiente. Nosso clipe pesava cerca de 7,3 MB, o que é demais para qualquer site razoável.Por isso, transformamos o clipe em um elemento de vídeo com dois arquivos de origem: um MP4 e um WebM para maior compatibilidade com navegadores.

Substituir GIFs animados por vídeo
Fig. 16. Substituir GIFs animados por vídeo

Usamos a ferramenta FFmpeg para converter o GIF animado em um arquivo MP4. O formato WebM oferece uma economia ainda maior, e a API ImageOptim pode fazer essa conversão para você.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Conseguimos economizar mais de 80% do peso geral graças a essa conversão. Isso reduziu o tamanho para cerca de 1 MB.

Ainda assim, 1 MB é um recurso grande para enviar pela rede, especialmente para um usuário com largura de banda restrita. Felizmente, podemos usar a API Effective Type para perceber que eles estão em uma largura de banda lenta e fornecer um JPEG muito menor.

Essa interface usa o tempo de retorno efetivo e os valores de download para estimar o tipo de rede que o usuário está usando. Ela simplesmente retorna uma string: slow 2G, 2G, 3G ou 4G. Dependendo desse valor, se o usuário estiver em uma conexão inferior a 4G, podemos substituir o elemento de vídeo pela imagem.

if (navigator.connection.effectiveType) { ... }

Isso prejudica um pouco a experiência, mas pelo menos o site pode ser usado em uma conexão lenta.

Carregar imagens fora da tela de forma lenta

Carrosséis, controles deslizantes ou páginas muito longas geralmente carregam imagens, mesmo que o usuário não possa vê-las na página imediatamente.

O Lighthouse vai sinalizar esse comportamento na auditoria de imagens fora da tela, e você também pode conferir isso no painel Network do DevTools. Se você notar que muitas imagens estão sendo carregadas, mas apenas algumas estão visíveis na página, considere usar o carregamento lento.

O carregamento lento ainda não é compatível de forma nativa com o navegador, então precisamos usar o JavaScript para adicionar esse recurso. Usamos a biblioteca Lazysizes para adicionar o comportamento de carregamento lento às capas do Oodle.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

O Lazysizes é inteligente porque não apenas rastreia as mudanças de visibilidade do elemento, mas também faz a pré-busca proativa de elementos que estão perto da visualização para otimizar a experiência do usuário. Ele também oferece uma integração opcional do IntersectionObserver, que oferece pesquisas de visibilidade muito eficientes.

Depois dessa mudança, nossas imagens são buscadas sob demanda. Se você quiser se aprofundar nesse assunto, confira o images.guide, um recurso muito útil e abrangente.

Ajudar o navegador a fornecer recursos essenciais com antecedência

Nem todos os bytes enviados ao navegador têm o mesmo grau de importância, e o navegador sabe disso. Muitos navegadores têm heurísticas para decidir o que deve ser buscado primeiro. Por isso, às vezes eles buscam o CSS antes de imagens ou scripts.

Algo que pode ser útil é nós, como autores da página, informarmos ao navegador o que é realmente importante para nós. Felizmente, nos últimos anos, os fornecedores de navegadores adicionaram vários recursos para nos ajudar com isso, como dicas de recursos, como link rel=preconnect, preload ou prefetch.

Esses recursos que foram trazidos para a plataforma da Web ajudam o navegador a buscar o que é certo no momento certo, e podem ser um pouco mais eficientes do que algumas das abordagens de carregamento personalizado baseadas em lógica que são feitas usando script.

Vamos ver como o Lighthouse nos orienta a usar alguns desses recursos de maneira eficaz.

A primeira coisa que o Lighthouse nos diz para fazer é evitar várias idas e voltas caras para qualquer origem.

Evite várias idas e voltas caras para qualquer origem
Fig. 17. Evite várias viagens de ida e volta caras para qualquer origem.

No caso do app Oodle, estamos usando bastante o Google Fonts. Sempre que você solta uma folha de estilo do Google Fonts na sua página, ela se conecta a até dois subdomínios. E o que o Lighthouse está nos dizendo é que, se pudéssemos aquecer essa conexão, poderíamos economizar até 300 milissegundos no tempo de conexão inicial.

Ao aproveitar o link rel preconnect, podemos mascarar essa latência de conexão.

Especialmente com algo como o Google Fonts, em que o CSS da fonte é hospedado em googleapis.com e os recursos de fonte são hospedados em Gstatic, isso pode ter um impacto muito grande. Então, aplicamos essa otimização e reduzimos algumas centenas de milissegundos.

A próxima sugestão do Lighthouse é pré-carregar solicitações importantes.

Pré-carregar solicitações de chave
Fig. 18. Pré-carregar solicitações importantes

<link rel=preload> é muito eficiente, ele informa ao navegador que um recurso é necessário como parte da navegação atual e tenta fazer com que o navegador o busque o mais rápido possível.

Aqui, o Lighthouse está dizendo que devemos pré-carregar nossos principais recursos de fontes da Web, porque estamos carregando duas fontes da Web.

O pré-carregamento em uma fonte da Web é assim: especificando rel=preload, você transmite as com o tipo de fonte e, em seguida, especifica o tipo de fonte que está tentando carregar, como woff2.

O impacto que isso pode ter na sua página é bastante nítido.

Impacto do pré-carregamento de recursos
Fig. 19. Impacto do pré-carregamento de recursos

Normalmente, sem usar o link rel preload, se as fontes da Web forem essenciais para sua página, o navegador precisa primeiro buscar o HTML, analisar o CSS e, muito mais tarde, buscar as fontes da Web.

Ao usar o link rel preload, assim que o navegador analisar o HTML, ele poderá começar a buscar essas fontes da Web muito antes. No caso do nosso app, isso reduziu em um segundo o tempo necessário para renderizar o texto usando nossas fontes da Web.

Não é tão simples assim se você tentar pré-carregar fontes usando o Google Fonts. Há um detalhe.

Os URLs do Google Fonts que especificamos nas nossas famílias de fontes nas folhas de estilo eram algo que a equipe de fontes atualiza com bastante regularidade. Esses URLs podem expirar ou ser atualizados com frequência. Por isso, sugerimos que você hospede suas próprias fontes da Web se quiser ter controle total sobre a experiência de carregamento de fontes. Isso pode ser ótimo porque dá acesso a coisas como link rel preload.

No nosso caso, achamos a ferramenta Google Web Fonts Helper muito útil para deixar algumas dessas fontes da Web off-line e configurá-las localmente. Confira essa ferramenta.

Se você estiver usando fontes da Web como parte dos seus recursos críticos, ou se for o caso do JavaScript, tente ajudar o navegador a entregar seus recursos críticos o mais rápido possível.

Experimental: dicas de prioridade

Temos uma novidade especial para compartilhar com você hoje. Além de recursos como dicas de recursos e pré-carregamento, estamos trabalhando em um novo recurso experimental do navegador que chamamos de dicas de prioridade.

Definir prioridade para o conteúdo inicialmente visível
Fig. 20. Dicas de prioridade

Esse é um novo recurso que permite indicar ao navegador a importância de um recurso. Ele expõe um novo atributo, "importance", com os valores "low", "high" ou "auto".

Isso permite reduzir a prioridade de recursos menos importantes, como estilos não críticos, imagens ou chamadas de API de busca, para reduzir a contenção. Também podemos aumentar a prioridade de coisas mais importantes, como as imagens principais.

No caso do app Oodle, isso levou a um lugar prático em que poderíamos otimizar.

Definir prioridade para o conteúdo inicialmente visível
Fig. 21. Defina a prioridade para o conteúdo inicialmente visível

Antes de adicionarmos o carregamento lento às nossas imagens, o navegador estava fazendo o seguinte: tínhamos esse carrossel de imagens com todos os nossos doodles, e o navegador estava buscando todas as imagens no início do carrossel com uma alta prioridade logo no início. Infelizmente, as imagens no meio do carrossel eram as mais importantes para o usuário. Então, definimos a importância dessas imagens de plano de fundo como muito baixa, as de primeiro plano como muito alta, e isso teve um impacto de dois segundos em 3G lento e na rapidez com que conseguimos buscar e renderizar essas imagens. Foi uma experiência positiva.

Esperamos disponibilizar esse recurso no Canary em algumas semanas. Fique de olho.

Tenha uma estratégia de carregamento de fontes da Web

A tipografia é fundamental para um bom design. Se você estiver usando fontes da Web, o ideal é não bloquear a renderização do texto e, definitivamente, não mostrar texto invisível.

Agora, destacamos isso no Lighthouse com a auditoria evitar texto invisível enquanto as fontes da Web estão sendo carregadas.

Evite texto invisível enquanto as fontes da Web estão carregando
Fig. 22. Evite texto invisível enquanto as fontes da Web estão sendo carregadas

Se você carregar as fontes da Web usando um bloco de fonte, o navegador vai decidir o que fazer se demorar muito para buscar essa fonte. Alguns navegadores esperam até três segundos antes de usar uma fonte do sistema e, por fim, trocam pela fonte correta quando ela é baixada.

Estamos tentando evitar esse texto invisível. Nesse caso, não seria possível ver os doodles clássicos desta semana se a fonte da Web demorasse muito para carregar. Felizmente, com um novo recurso chamado font-display, você tem muito mais controle sobre esse processo.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

A exibição de fontes ajuda a decidir como as fontes da Web serão renderizadas ou substituídas com base no tempo que leva para serem trocadas.

Nesse caso, estamos usando a troca de exibição de fontes. A troca dá à fonte um período de bloqueio de zero segundo e um período de troca infinito. Isso significa que o navegador vai desenhar o texto imediatamente com uma fonte substituta se a fonte demorar para carregar. E vai trocá-lo quando a fonte estiver disponível.

No caso do nosso app, isso foi ótimo porque permitiu exibir um texto significativo bem no início e fazer a transição para a fonte da Web quando ela estava pronta.

Resultado da exibição de fontes
Fig. 23. Resultado da exibição de fontes

Em geral, se você estiver usando fontes da Web, como uma grande porcentagem da Web faz, tenha uma boa estratégia de carregamento de fontes da Web.

Há muitos recursos de plataforma da Web que você pode usar para otimizar a experiência de carregamento de fontes, mas também confira o repositório de receitas de fontes da Web de Zach Leatherman, porque ele é muito bom.

Reduzir scripts que bloqueiam a renderização

Há outras partes do aplicativo que poderíamos enviar antes na cadeia de download para oferecer pelo menos uma experiência básica ao usuário um pouco mais cedo.

Na faixa da linha do tempo do Lighthouse, é possível ver que, durante esses primeiros segundos, quando todos os recursos estão sendo carregados, o usuário não consegue ver nenhum conteúdo.

Oportunidade de reduzir folhas de estilo que bloqueiam a renderização
Fig. 24. Reduzir a oportunidade de folhas de estilo que bloqueiam a renderização

O download e o processamento de folhas de estilo externas estão impedindo que o processo de renderização prossiga.

Podemos tentar otimizar nosso caminho crítico de renderização entregando alguns dos estilos um pouco antes.

Se extrairmos os estilos responsáveis por essa renderização inicial e os incorporarmos ao HTML, o navegador poderá renderizá-los imediatamente, sem esperar que as folhas de estilo externas cheguem.

No nosso caso, usamos um módulo NPM chamado Critical para incorporar nosso conteúdo crítico em index.html durante uma etapa de build.

Embora esse módulo tenha feito a maior parte do trabalho pesado, ainda foi um pouco difícil fazer isso funcionar sem problemas em diferentes rotas.

Se você não tomar cuidado ou se a estrutura do seu site for muito complexa, pode ser muito difícil introduzir esse tipo de padrão se você não planejou a arquitetura de shell do app desde o início.

Por isso, é tão importante considerar a performance desde o início. Se você não projetar para performance desde o início, é muito provável que tenha problemas para fazer isso mais tarde.

No final, o risco valeu a pena, conseguimos fazer funcionar e o app começou a entregar conteúdo muito mais cedo, melhorando significativamente o tempo da First Meaningful Paint.

O resultado

Essa foi uma longa lista de otimizações de desempenho que aplicamos ao nosso site. Vamos analisar o resultado. É assim que nosso app é carregado em um dispositivo móvel médio em uma rede 3G, antes e depois da otimização.

A pontuação de desempenho do Lighthouse subiu de 23 para 91. É um progresso muito bom em termos de velocidade. Todas as mudanças foram impulsionadas pela verificação e pelo acompanhamento contínuos do relatório do Lighthouse. Se você quiser conferir como implementamos tecnicamente todas as melhorias, fique à vontade para acessar nosso repositório, especialmente as solicitações de envio que foram feitas.

Performance preditiva: experiências do usuário baseadas em dados

Acreditamos que o machine learning representa uma oportunidade empolgante para o futuro em muitas áreas. Uma ideia que esperamos que incentive mais experimentação no futuro é que dados reais podem realmente orientar as experiências do usuário que estamos criando.

Hoje, tomamos muitas decisões arbitrárias sobre o que o usuário pode querer ou precisar e, portanto, o que vale a pena ser pré-buscado, pré-carregado ou pré-armazenado em cache. Se acertarmos, poderemos priorizar uma pequena quantidade de recursos, mas é muito difícil escalonar isso para todo o site.

Temos dados disponíveis para melhorar nossas otimizações hoje. Usando a API Google Analytics Reporting, podemos analisar as próximas páginas principais e as porcentagens de saída de qualquer URL no site e, assim, tirar conclusões sobre quais recursos devemos priorizar.

Se combinarmos isso com um bom modelo de probabilidade, evitaremos o desperdício de dados do usuário com a pré-busca excessiva de conteúdo. Podemos aproveitar esses dados do Google Analytics e usar o aprendizado de máquina e modelos como cadeias de Markov ou rede neural para implementar esses modelos.

Pacotes baseados em dados para apps da Web
Fig. 25. Pacotes com base em dados para apps da Web

Para facilitar esses experimentos, temos o prazer de anunciar uma nova iniciativa chamada Guess.js.

Guess.js
Fig. 26. Guess.js

O Guess.js é um projeto focado em experiências do usuário baseadas em dados para a Web. Esperamos que isso inspire a exploração do uso de dados para melhorar o desempenho da Web e ir além. Tudo isso é de código aberto e está disponível no GitHub hoje mesmo. Ele foi criado em colaboração com a comunidade de código aberto por Minko Gechev, Kyle Matthews do Gatsby, Katie Hempenius e vários outros.

Confira o Guess.js e conte o que achou.

Resumo

As pontuações e métricas são úteis para melhorar a velocidade da Web, mas são apenas os meios, não as metas em si.

Todos nós já passamos por carregamentos lentos de páginas em dispositivos móveis, mas agora temos a oportunidade de oferecer aos usuários experiências mais agradáveis que carregam muito rapidamente.

Melhorar a performance é uma jornada. Muitas pequenas mudanças podem levar a grandes ganhos. Ao usar as ferramentas de otimização certas e ficar de olho nos relatórios do Lighthouse, você pode oferecer uma experiência melhor e mais inclusiva aos usuários.

Agradecimentos especiais a: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse e Google Doodles.