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 o aprimoramento do desempenho na Web. Aqui, explicamos isso usando o app Oodles Theater. Também falamos sobre nossos experimentos com carregamento preditivo e a nova iniciativa Guess.js.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Estivemos muito ocupados no ano passado tentando descobrir como tornar a Web mais rápida e mais 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, não é possível 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 o desempenho é importante para os usuários, mas também pode parecer um segredo para descobrir por onde começar a otimizar. 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 ter certeza de que ele fosse o melhor desempenho possível. O ponto de partida da 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 aguardar 15 segundos pela primeira pintura significativa ou pela interação do app. 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. Compacte e compacte 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, então, para a minificação, simplesmente usamos o plug-in JS do Uglify.

A minificação é uma tarefa comum. Portanto, você poderá 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.

Estávamos usando o Firebase Hosting para hospedar nosso código, e o Firebase ativa 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 tem suporte na maioria dos navegadores, e você pode usar um binário para pré-compactar seus recursos antes de enviá-los ao servidor.

Use 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 máxima no nosso servidor, garantimos que, em uma visita repetida, o usuário possa reutilizar os recursos que já baixou 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, estávamos usando a biblioteca Material Components para prototipar nosso app rapidamente. Com o tempo, mudamos para uma aparência mais personalizada e esquecemos totalmente 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 de baixo. Mais de 95% do CSS e um monte de JavaScript não foram usados.

O Lighthouse também detectou esse problema na auditoria de regras CSS não usadas. Ele mostrou uma economia potencial de mais de 400 KB. Voltamos ao código e removemos as partes de JavaScript e CSS dessa biblioteca.

Se o adaptador MVC for descartado, 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 deixou nosso pacote de CSS 20 vezes mais baixo, o que é muito bom para um commit pequeno de duas linhas.

Obviamente, isso aumentou nossa pontuação de desempenho e também o tempo para interação ficou muito melhor.

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, é importante sempre ficar de olho em 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 de páginas da Web 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 veiculado aos usuários, medir o desempenho deles e decidir se vale a pena implementar com a experiência inicial. À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 JavaScript, tivemos a sorte de ter um amplo conjunto de ferramentas de auditoria de pacotes JavaScript.

Auditoria de pacotes JavaScript
Figura 9. Auditoria de pacotes JavaScript

Começamos com o analisador de pacotes webpack, que nos informou que estávamos incluindo uma dependência chamada unicode, que tem 1,6 MB de JavaScript analisado, e é muita coisa.

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 estava referenciando este módulo.

Depois, mudamos para outra ferramenta, a BundlePhobia. Essa é uma ferramenta que permite inserir o nome de qualquer pacote do NPM e conferir a estimativa do tamanho minificado e do gzip. Encontramos uma boa alternativa para o módulo de slug que estávamos usando que pesava apenas 2,2 KB, então mudamos isso.

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 é seu 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 de 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 celular 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 da rota ou do 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 de 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. Diminuiu 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 para o usuário.

Aproveite conceitos como divisão de código, explore ideias como o tree shaking e confira o repositório webpack-libs-optimizations para conferir algumas ideias sobre como reduzir o tamanho da biblioteca caso você esteja 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, falhamos nas três auditorias relacionadas a imagens.

Esquecemos de otimizar nossas imagens, não fazíamos o dimensionamento correto delas e também podíamos ganhar 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.

Dessa forma, você garante que as imagens adicionadas no futuro sejam otimizadas automaticamente. Alguns CDNs, por exemplo, o 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 janela de visualização e executá-lo pelo 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 destinado a 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 maior suporte a navegadores.

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

Usamos a ferramenta FFmpeg para converter o GIF de animação no arquivo MP4. O formato WebM oferece uma economia ainda maior. 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 nos fez chegar a 1 MB.

Ainda assim, 1 MB é um recurso grande para enviar, especialmente para um usuário com uma largura de banda restrita. Felizmente, podemos usar a API Effective Type para perceber que eles estão em uma largura de banda lenta e receber 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 lenta 2G, 2G, 3G ou 4G. Dependendo desse valor, se o usuário estiver abaixo da rede 4G, poderemos substituir o elemento de vídeo pela imagem.

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

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

Carregamento lento de 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 esse recurso. 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.

Após essa mudança, nossas imagens serão 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 buscarão CSS antes de imagens ou scripts.

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

Esses recursos 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 personalizadas de carregamento baseada em lógica que são feitas com scripts.

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

A primeira coisa que o Lighthouse diz a fazer é evitar várias idas e voltas caras a 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 da fonte do Google na sua página, ela será conectada a dois subdomínios. O que o Lighthouse nos diz é que, se pudéssemos aquecer essa conexão, poderíamos 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. Assim, aplicamos essa otimização e economizamos algumas centenas de milissegundos.

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

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

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.

O Lighthouse está nos informando 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 disso na sua página é bastante forte.

Impacto do pré-carregamento de recursos
Fig. 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á uma pegadinha.

A equipe de fontes atualiza com bastante frequência os URLs das fontes do Google que especificamos nos nossos tipos de fonte nas folhas de estilo. Esses URLs podem expirar ou serem atualizados em uma frequência regular. Portanto, se você quiser controle total sobre a experiência de carregamento de fontes, sugerimos hospedar suas 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, descobrimos que a ferramenta Google Web Fonts Helper é muito útil para nos ajudar off-line 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 de navegador chamado dicas de prioridade.

Definir a prioridade do conteúdo visível inicialmente
Figura 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 hero.

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

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

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. O que fizemos foi definir a importância dessas imagens de plano de fundo como muito baixa, e as em primeiro plano, muito altas. Isso teve um impacto de dois segundos em relação ao 3G lento e a rapidez com que conseguimos buscar e renderizar essas imagens. Então, foi 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ê usa fontes da Web, o ideal é não bloquear a renderização do texto nem 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 textos invisíveis 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 fonte. 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, o motivo pelo qual isso foi ótimo é que nos permitiu mostrar um texto significativo logo no 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 usa, tenha uma boa estratégia de carregamento de fontes da Web em vigor.

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 acessar nenhum conteúdo.

Reduza a oportunidade das folhas de estilo que bloqueiam a renderização
Fig. 24. Reduza as oportunidades das folhas de estilo de bloqueio de 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 do NPM chamado Critical para colocar nosso conteúdo essencial em index.html durante uma etapa do 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 a estrutura do site for muito complexa, pode ser muito difícil introduzir esse tipo de padrão se 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

Foi uma longa lista de otimizações de desempenho que aplicamos ao 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 verificando e seguindo 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 orientadas por 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, vale a pena ser pré-buscado, pré-carregado ou 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.

De fato, temos dados disponíveis hoje para fundamentar melhor 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 machine learning e modelos como cadeias de Markov ou rede neural para implementar esses modelos.

Pacotes baseados em 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 hoje. 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 nos diga o que você 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 passamos por carregamentos de página lentos em qualquer lugar, mas agora temos a oportunidade de oferecer aos usuários experiências mais agradáveis e rápidas.

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.