O desempenho da Web ficou mais fácil - 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 da Web. Aqui, explicamos isso usando o app Oodles Theater. Também falamos sobre nossos experimentos com carregamento preditivo e a nova iniciativa Guess.js.

Ewa Gasperowicz

No ano passado, estivemos bastante ocupados tentando descobrir como tornar a Web mais rápida e eficiente. 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 mais pesada a cada ano. Se verificarmos o estado da Web, veremos que uma página média em dispositivos móveis pesa cerca de 1,5 MB, sendo a maioria 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 desnecessário, contribui para o quebra-cabeças complicado de performance.

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

Hierarquia de UX em forma de pirâmide
Figura 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 também pode parecer um segredo descobrir por onde começar a otimização. Felizmente, há ferramentas que podem ajudar você.

Lighthouse: uma base para o fluxo de trabalho de performance

O Lighthouse é uma parte do Chrome DevTools que permite fazer uma auditoria do seu site e oferece 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
Figura 2. Novas auditorias do Lighthouse

Vamos conferir como 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 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
Figura 3. Relatório do Lighthouse para o app Oodles

O desempenho inicial do nosso app, como mostrado no relatório do Lighthouse, foi muito ruim. Em uma rede 3G, o usuário precisava esperar 15 segundos para a primeira pintura significativa ou para o app ficar interativo. O Lighthouse destacou muitos problemas com nosso site, e a pontuação de desempenho geral de 23 refletiu exatamente isso.

A página pesava cerca de 3,4 MB.Precisávamos desesperadamente cortar alguns excessos.

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

Oportunidades de otimização de desempenho

Remover recursos desnecessários

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

Benefícios da minificação
Figura 4. Minificar e compactar JavaScript e CSS

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

A minificação é uma tarefa comum. Por isso, você pode encontrar uma solução pronta para qualquer processo de build que usar.

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 oferece suporte a isso de forma imediata.

Usamos o Firebase Hosting para hospedar nosso código, e o Firebase ativa o gzip por padrão. Portanto, por 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 tem suporte na 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 notar que poderíamos otimizar nossas estratégias de cache para conseguir exatamente isso. Ao definir um cabeçalho de expiração max-age no nosso servidor, garantimos que, em uma visita repetida, o usuário pudesse reutilizar os recursos que ele havia feito o download antes.

O ideal é armazenar no 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 que foram 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
Figura 5. Verificar a cobertura do código

Às vezes, incluímos código nos apps que não é realmente necessário. 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 de lado. Foi exatamente isso que aconteceu com a gente.

No início, usamos a biblioteca Material Components para criar protótipos do app rapidamente. Com o tempo, passamos a usar um visual e uma sensação mais personalizados e nos esquecemos completamente dessa biblioteca. Felizmente, a verificação de cobertura de código nos ajudou a encontrá-lo no nosso pacote.

É possível verificar as estatísticas de cobertura de código no DevTools, tanto para o tempo 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 CSS e um monte de JavaScript não estavam sendo usados.

O Lighthouse também detectou esse problema na auditoria de regras CSS não usadas. Ele mostrou um possível poupança de mais de 400 KB. Então, voltamos ao nosso código e removemos a parte do JavaScript e do CSS dessa biblioteca.

Se você remover o adaptador MVC, os estilos serão reduzidos para 10 KB
Figura 6. Se você remover o adaptador MVC, os estilos serão reduzidos para 10 KB.

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

Isso fez com que nossa pontuação de desempenho aumentasse, e o Tempo para Interatividade melhorou muito.

No entanto, com mudanças como essa, não basta verificar as métricas e as pontuações. A remoção de código real nunca é isenta de riscos. Por isso, é preciso sempre ficar atento a possíveis regressões.

Nosso código não foi usado em 95%. Ainda há 5% em algum lugar. Aparentemente, um dos nossos componentes ainda usava os estilos dessa biblioteca, as pequenas setas no controle deslizante de rabiscos. Como era muito pequeno, poderíamos incorporar manualmente esses estilos de volta aos botões.

Os botões foram corrompidos por uma biblioteca ausente.
Figura 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 se proteger contra possíveis regressões visuais.

Evite payloads de rede muito grandes

Sabemos que recursos grandes podem deixar o carregamento da página mais lento. Elas podem custar dinheiro aos nossos usuários e ter um grande impacto nos planos de dados. Por isso, é muito importante estar atento a isso.

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

Detectar payloads de rede enormes
Figura 8. Detectar payloads de rede muito grandes

Aqui, notamos 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 JavaScript de 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 é a que não é feita.

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

No nosso caso, como lidamos com muitos pacotes JavaScript, tivemos sorte porque a comunidade JavaScript tem um conjunto rico de ferramentas de auditoria de pacotes JavaScript.

Auditoria de pacotes do JavaScript
Figura 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 era de 1,6 MB de JavaScript analisado, ou seja, bastante.

Em seguida, acessamos o editor e, usando o Import Cost Plugin for Visual code, visualizamos o custo de cada módulo que estávamos importando. Isso nos permitiu descobrir qual componente estava incluindo o código que referenciava esse módulo.

Depois, mudamos para outra ferramenta, a BundlePhobia. Essa é uma ferramenta que permite inserir o nome de qualquer pacote do NPM e conferir qual é o tamanho estimado minificado e compactado por 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 no nosso desempenho. Entre essa mudança e a descoberta de outras oportunidades para reduzir o tamanho do pacote de JavaScript, economizamos 2,1 MB de código.

Observamos uma melhoria de 65% no geral, considerando o tamanho compactado e minificado desses pacotes. E descobrimos que valeu a pena fazer isso como um processo.

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, audite seus recursos com frequência.

Tempo de inicialização do JavaScript mais rápido com a divisão de código

Embora payloads de rede grandes possam ter um grande impacto no app, há outra coisa que pode ter um impacto muito grande, que é o JavaScript.

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

É assim que um navegador processa JavaScript.

Processamento de JavaScript
Figura 10. Processamento do JavaScript

Primeiro, precisamos fazer o download do script. Temos um mecanismo JavaScript que precisa analisar esse código, além de compilá-lo e executá-lo.

Essas fases não levam muito tempo em um dispositivo de última geração, como um computador desktop ou um laptop, talvez até um smartphone de última geração. Mas em um smartphone mediano, esse processo pode levar de cinco a dez vezes mais tempo. Isso é o que atrasa a interatividade. Portanto, é importante tentar reduzir isso.

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

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

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

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

A divisão de código é como pizza

O conceito de divisão de código é o seguinte: em vez de fornecer aos usuários uma pizza inteira de JavaScript, por que não fornecer apenas uma fatia de cada vez, conforme a necessidade?

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

Incorporamos a divisão de código ao nosso aplicativo, mudamos de importações estáticas para importações dinâmicas, permitindo que carregássemos o código de forma assíncrona conforme necessário.

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

O impacto disso foi reduzir o tamanho dos nossos pacotes e também diminuir o tempo de inicialização do JavaScript. O tempo caiu para 0,78 segundos, 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 para o usuário.

Aproveite conceitos como divisão de código, explore ideias como o descarte de árvores e confira o repo webpack-libs-optimizations para algumas ideias sobre como reduzir o tamanho da biblioteca se você estiver usando o webpack.

Otimizar imagens

Piada sobre a performance de carregamento de imagens

No app Oodle, usamos muitas imagens. Infelizmente, o Lighthouse estava muito menos entusiasmado com isso do que nós. Na verdade, não conseguimos concluir as três auditorias relacionadas a imagens.

Esquecemos de otimizar nossas imagens, não estávamos dimensionando corretamente e também conseguimos algum ganho usando outros formatos de imagem.

Auditorias de imagem
Figura 14. Auditorias de imagem 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, as imagens adicionadas no futuro serão otimizadas automaticamente. Alguns CDNs, por exemplo, 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 hospedar suas imagens nesses serviços.

Se você não quiser fazer isso por questões de custo ou latência, projetos como o Thumbor ou o Imageflow oferecem alternativas auto-hospedadas.

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

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

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

Use o formato certo para conteúdo animado

Os GIFs podem ficar 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 grandes economias em termos de tamanho de arquivo.

No app Oodle, usamos um GIF como uma sequência de introdução na página inicial. De acordo com o Lighthouse, poderíamos economizar mais de 7 MB ao mudar para um formato de vídeo mais eficiente. Nosso clipe pesava cerca de 7,3 MB, muito 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 oferecer suporte a mais navegadores.

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

Usamos a ferramenta FFmpeg para converter o GIF de animação no arquivo MP4. O formato WebM oferece economias ainda maiores. 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 nosso peso total graças a essa conversão. Isso reduziu para cerca de 1 MB.

Ainda assim, 1 MB é um recurso grande para enviar, especialmente para um usuário com uma largura de banda restrita. Por sorte, podemos usar a API Effective Type para saber se a largura de banda é lenta e fornecer um JPEG muito menor.

Essa interface usa o tempo de ida e volta efetivo e os valores de redução para estimar o tipo de rede que o usuário está usando. Ele simplesmente retorna uma string, 2G lento, 2G, 3G ou 4G. Portanto, dependendo desse valor, se o usuário estiver abaixo de 4G, poderíamos substituir o elemento de vídeo pela imagem.

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

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

Carregar lentamente imagens fora da tela

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

O Lighthouse sinaliza esse comportamento na auditoria de imagens fora da tela, e você também pode conferir no painel de rede do DevTools. Se você notar que há muitas imagens chegando enquanto apenas algumas estão visíveis na página, talvez seja melhor usar o carregamento lento.

O carregamento lento ainda não tem suporte nativo no navegador. Por isso, precisamos usar o JavaScript para adicionar essa capacidade. Usamos a biblioteca Lazysizes para adicionar o comportamento de carregamento lento às nossas 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 próximos à visualização para uma experiência de usuário ideal. Ele também oferece uma integração opcional do IntersectionObserver, que oferece pesquisas de visibilidade muito eficientes.

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

Ajude o navegador a entregar recursos essenciais com antecedência

Nem todo byte enviado tem o mesmo grau de importância, e o navegador sabe disso. Muitos navegadores têm heurísticas para decidir o que eles precisam buscar primeiro. Por isso, às vezes, eles vão buscar CSS antes de imagens ou scripts.

Algo que pode ser útil é informarmos, como autores da página, ao navegador o que é realmente importante para nós. Felizmente, nos últimos anos, os fornecedores de navegadores têm adicionado vários recursos para 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 a coisa certa no momento certo e podem ser um pouco mais eficientes do que algumas das abordagens de carregamento personalizado com base na lógica que são feitas usando script.

Vamos conferir como o Lighthouse nos orienta a usar alguns desses recursos de forma eficaz.

A primeira coisa que o Lighthouse nos diz para fazer é evitar várias viagens de ida e volta caras para qualquer origem.

Evite viagens de ida e volta caras para qualquer origem
Figura 17. Evite viagens de ida e volta caras para qualquer origem.

No caso do app Oodle, usamos muito as fontes do Google. Sempre que você soltar uma folha de estilo de fonte do Google na página, ela vai se conectar a dois subdomínios. O Lighthouse está dizendo que, se conseguirmos aquecer essa conexão, poderemos economizar até 300 milissegundos no tempo de conexão inicial.

Usando a pré-conexão de link rel, podemos mascarar essa latência de conexão.

Isso pode ter um grande impacto, especialmente em algo como as fontes do Google, em que o CSS da fonte é hospedado em googleapis.com e os recursos de fonte são hospedados em Gstatic. Aplicamos essa otimização e reduzimos algumas centenas de milissegundos.

A próxima coisa que o Lighthouse sugere é pré-carregar solicitações importantes.

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

O <link rel=preload> é muito poderoso. 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 precisamos pré-carregar nossos principais recursos de fonte 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 disso na sua página é bastante forte.

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

Normalmente, sem usar o pré-carregamento de link rel, se as fontes da Web forem essenciais para a página, o que o navegador precisa fazer é, em primeiro lugar, buscar o HTML, analisar o CSS e, mais tarde, buscar as fontes da Web.

Usando o pré-carregamento de link rel, assim que o navegador analisar seu HTML, ele poderá começar a buscar essas fontes da Web muito mais cedo. No caso do nosso app, isso reduziu um segundo do tempo que levava para renderizar texto usando nossas fontes da Web.

Não é tão simples tentar carregar fontes com o Google Fonts. Há um problema.

Os URLs das fontes do Google que especificamos nas nossas fontes nas folhas de estilo são atualizados com frequência pela equipe de fontes. Esses URLs podem expirar ou ser atualizados com frequência. Portanto, se você quiser ter controle total sobre a experiência de carregamento de fontes, sugerimos que você hospede as fontes da Web por conta própria. Isso pode ser ótimo porque dá acesso a recursos como o pré-carregamento de rel de link.

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

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

Experimental: dicas de prioridade

Temos algo 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 chamado dicas de prioridade.

Definir a prioridade do conteúdo visível inicialmente
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 nos permite transmitir a redução da prioridade de recursos menos importantes, como estilos, imagens ou chamadas de API não críticas, para reduzir a contenção. Também podemos aumentar a prioridade de coisas mais importantes, como nossas imagens principais.

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

Definir a prioridade do conteúdo visível inicialmente
Figura 21. Defina a prioridade do conteúdo visível inicialmente

Antes de adicionar o carregamento lento às imagens, o navegador usava um carrossel de imagens com todos os doodles, e ele buscava todas as imagens no início do carrossel com alta prioridade. 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 relação ao 3G lento e a rapidez com que conseguimos buscar e renderizar essas imagens. Uma experiência positiva.

Esperamos lançar esse recurso na versão 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, não bloqueie a renderização do texto e não mostre 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 sendo carregadas
Figura 22. Evite textos invisíveis 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 a busca da fonte da Web demorar muito. Alguns navegadores vão esperar até três segundos para isso antes de usar uma fonte do sistema e, eventualmente, trocar por ela depois que for feita o download.

Estamos tentando evitar esse texto invisível. Nesse caso, não seria possível ver os doodles clássicos da semana se a fonte da Web demorasse muito. 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 necessário para a troca.

Neste caso, estamos usando a troca de exibição de fontes. A troca dá ao tipo de fonte um período de bloqueio de zero segundo e um período de troca infinito. Isso significa que o navegador vai exibir o texto imediatamente com uma fonte de fallback se ela demorar para carregar. E ele vai ser trocado quando a fonte estiver disponível.

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

Resultado da exibição da fonte
Figura 23. Resultado da exibição da fonte

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

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

Reduzir scripts que bloqueiam a renderização

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

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

Reduzir a oportunidade de 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 o processo de renderização de fazer qualquer progresso.

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

Se extrairmos os estilos responsáveis por essa renderização inicial e os colocarmos inline no 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 para nós, ainda era um pouco complicado fazer isso funcionar bem em diferentes rotas.

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

É por isso que é tão importante considerar a performance desde o início. Se você não projetar para performance desde o início, há uma grande chance de ter problemas mais tarde.

No final, nosso risco valeu a pena, conseguimos fazer com que funcionasse e o app começou a entregar conteúdo muito mais cedo, melhorando significativamente o primeiro tempo de pintura significativo.

O resultado

Essa foi uma longa lista de otimizações de desempenho que aplicamos ao nosso site. Vamos conferir o resultado. Confira como nosso app foi 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 aumentou de 23 para 91. Esse é um progresso muito bom em termos de velocidade. Todas as mudanças foram impulsionadas por nós, que verificamos e seguimos continuamente o relatório do Lighthouse. Se você quiser conferir como implementamos tecnicamente todas as melhorias, confira nosso repositório, especialmente as PRs que foram enviadas para ele.

Desempenho preditivo: experiências do usuário com base em dados

Acreditamos que o aprendizado de máquina representa uma oportunidade incrível para o futuro em muitas áreas. Uma ideia que esperamos que incentive mais experimentos no futuro é que dados reais podem 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 a suposição estiver correta, poderemos priorizar uma pequena quantidade de recursos, mas é muito difícil dimensioná-los para todo o site.

Temos dados disponíveis para melhorar nossas otimizações. Usando a API Google Analytics Reporting, é possível conferir a próxima página principal e as porcentagens de saída de qualquer URL no nosso site e, portanto, gerar conclusões sobre quais recursos devem ser priorizados.

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

Agrupamento orientado a dados para apps da Web
Figura 25. Agrupamento orientado a dados para apps da Web

Para facilitar esses experimentos, anunciamos 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 ele inspire a exploração do uso de dados para melhorar o desempenho da Web e ir além. Tudo é de código aberto e está disponível no GitHub. 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 ajudam a melhorar a velocidade da Web, mas são apenas meios, não as metas em si.

Todos nós já tivemos problemas com carregamento lento de páginas em trânsito, mas agora temos a oportunidade de oferecer aos nossos usuários experiências mais agradáveis que carregam muito rápido.

Melhorar a performance é uma jornada. Muitas pequenas mudanças podem gerar 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.

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