O desempenho da Web ficou mais fácil - Google I/O 2018

No Google I/O 2018, apresentamos um resumo das ferramentas, bibliotecas e técnicas de otimização que facilitam a melhoria do desempenho da Web. Aqui, elas serão explicadas 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 bastante ocupados no último ano tentando descobrir como tornar a web mais rápida e melhor. 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.

Necessidade de desempenho

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

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

A maioria dos usuários considera a velocidade como uma prioridade na hierarquia de UX. Isso não é muito surpreendente, porque não é possível fazer muitas coisas até que uma página termine de carregar. Não é possível der valor da página nem admirar a estética dela.

Piramida de hierarquia de UX
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 descobrir onde começar a otimizar. Felizmente, existem ferramentas que podem ajudar você nesse caminho.

Lighthouse: uma base para o fluxo de trabalho de desempenho

O Lighthouse faz parte do Chrome DevTools que permite fazer uma auditoria do seu site e dar dicas sobre como melhorar.

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

Novas auditorias do Lighthouse
Figura 2. Novas auditorias do Lighthouse

Vejamos como você pode aproveitá-los 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é mesmo jogar um ou dois jogos.

Durante o desenvolvimento do app, queríamos garantir que ele tivesse a melhor performance 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 aplicativo, conforme visto no relatório do Lighthouse, foi muito ruim. Em uma rede 3G, o usuário precisava esperar 15 segundos pela primeira exibição significativa ou até que o app ficasse interativo. O Lighthouse destacou uma variedade de problemas com nosso site, e a pontuação de desempenho geral de 23 reflete exatamente isso.

A página pesava cerca de 3,4 MB.Precisávamos desesperadamente reduzir um pouco de gordura.

Isso começou nosso primeiro desafio de desempenho: encontrar coisas que possam ser removidas facilmente sem afetar a experiência geral.

Oportunidades de otimização da performance

Remover recursos desnecessários

Alguns itens óbvios que podem ser removidos com segurança: espaços em branco e comentários.

Ganhos da minificação
Figura 4. Minifique e compacte JavaScript e CSS

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

A minificação é uma tarefa comum, então é possível encontrar uma solução pronta para qualquer processo de compilação que você usar.

Outra auditoria útil é Ativar a compactação de texto. Não há motivo para enviar arquivos descompactados, e a maioria das CDNs já tem suporte imediato para isso.

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

Embora o gzip seja uma forma muito conhecida de compactação, outros mecanismos, como Zopfli e Brotli (links em inglês), 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

Nossa próxima etapa foi garantir que os recursos não sejam enviados duas vezes se isso for desnecessário.

A auditoria da política de cache ineficiente no Lighthouse nos ajudou a perceber que poderíamos otimizar nossas estratégias de armazenamento em cache para conseguir exatamente isso. Ao definir um cabeçalho de expiração max-age no servidor, garantimos que, em uma visita repetida, o usuário possa reutilizar os recursos que já baixou.

O ideal é armazenar o maior número possível de recursos em cache com a maior segurança possível 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
Figura 5. Verificar a cobertura do código

Às vezes, incluímos código no app que não é realmente necessário. Isso acontece especialmente se você trabalha no app por um período mais longo, sua equipe ou suas dependências mudam e, às vezes, uma biblioteca órfã é deixada para trás. Foi exatamente o que aconteceu conosco.

No início, estávamos usando a biblioteca de componentes do Material Design para prototipar rapidamente nosso app. Com o tempo, passamos para uma aparência mais personalizada e esquecemos completamente dessa biblioteca. Felizmente, a verificação da cobertura do código nos ajudou a redescobrir o código no nosso pacote.

É possível verificar as estatísticas de cobertura do código no DevTools, tanto para o ambiente de execução quanto para o tempo de carregamento do aplicativo. Veja as duas grandes faixas vermelhas na captura de tela na parte de baixo. Mais de 95% do nosso CSS não foi usado, além de muito JavaScript.

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

Se reduzirmos o adaptador MVC, os estilos caem para 10 KB
Figura 6. Se reduzirmos o adaptador MVC, os estilos caem 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 também melhorou o tempo para interação da página (link em inglês).

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

Nosso código não foi usado em 95%. Ainda restam esses 5%. Aparentemente, um dos nossos componentes ainda estava usando os estilos dessa biblioteca: as pequenas setas no controle deslizante. Como ela era tão pequena, poderíamos simplesmente incorporar esses estilos manualmente de volta aos botões.

Os botões foram corrompidos pela biblioteca ausente
Figura 7. Um componente ainda estava usando a biblioteca removida.

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

Evitar payloads de rede muito grandes

Sabemos que recursos grandes podem retardar o carregamento de páginas da Web. Eles podem custar dinheiro aos nossos usuários e ter um grande impacto nos planos de dados, por isso é muito importante estar ciente disso.

O Lighthouse detectou um problema com alguns dos nossos payloads de rede usando a auditoria de payload de rede enorme.

Detectar payloads de rede enormes
Figura 8. Detecte payloads de rede enormes

Aqui observamos que tínhamos mais de 3 MB de código sendo enviados, o que é uma quantidade muito grande, principalmente 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: o pedido mais rápido é aquele que não é feito.

O ideal é avaliar o valor de cada recurso oferecido aos usuários, medir o desempenho deles e verificar se vale a pena enviar 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 sorte porque a comunidade JavaScript tem um conjunto avançado de ferramentas de auditoria de pacotes JavaScript.

Auditoria de pacotes JavaScript
Figura 9. Auditoria de pacotes JavaScript

Começamos com o webpack Analyzer, que informou que estávamos incluindo uma dependência chamada unicode, que tinha 1,6 MB de JavaScript analisado.Portanto, essa é uma grande quantidade.

Em seguida, fomos ao nosso editor e, usando o plug-in de custo de importação para código visual, conseguimos visualizar 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, o BundlePhobia. É uma ferramenta que permite inserir o nome de qualquer pacote NPM e ver de fato o tamanho estimado de tamanho minificado e gzipado. Encontramos uma boa alternativa para o módulo 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 nosso pacote JavaScript, economizamos 2,1 MB de código.

Observamos melhorias de 65% no geral, depois que você leva em consideração o tamanho reduzido e compactado de gzip desses pacotes. Descobrimos que valia a pena fazer isso como um processo.

Por isso, em geral, tente eliminar downloads desnecessários dos seus sites e aplicativos. Faça um inventário dos seus recursos e meça o impacto deles na performance pode fazer uma grande diferença. Portanto, audite seus recursos com regularidade.

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

Grandes payloads de rede podem ter um grande impacto no nosso app, mas há outro elemento que pode ter um grande impacto: o JavaScript.

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

É assim que um navegador processa JavaScript.

Processamento de JavaScript
Figura 10. Processamento de JavaScript

Primeiro, temos que fazer o download desse script. Temos um mecanismo JavaScript que precisa analisar esse código e compilá-lo e executá-lo.

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

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

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

E, no caso do app Oodle, ele informou que tínhamos 1,8 segundo de tempo gasto na inicialização do JavaScript. O que estava acontecendo era que estávamos importando estaticamente todas as nossas rotas e componentes para um pacote JavaScript monolítico.

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

A divisão de código é igual à pizza

A divisão de código é essa noção de, em vez de dar a seus usuários todo o valor de JavaScript, e se você desse a eles apenas uma fatia por vez, quando 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 no aplicativo e mudamos de importações estáticas para importações dinâmicas, permitindo o carregamento lento de código de maneira 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 a redução do tamanho dos pacotes e o tempo de inicialização do JavaScript. A redução chegou a 0,78 segundo, tornando o app 56% mais rápido.

Em geral, se você estiver criando uma experiência com muito JavaScript, envie o código apenas para o usuário de que ele precisa.

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

Otimizar imagens

Piada sobre o carregamento de imagem

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 as imagens, não estávamos dimensionando corretamente e também poderíamos conseguir algum benefício com o uso de outros formatos de imagem.

Auditorias de imagens
Figura 14. Auditorias de imagens do Lighthouse

Começamos com a otimização das imagens.

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

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

Dessa forma, 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, é possível simplesmente hospedar suas imagens nesses serviços.

Se você não quiser fazer isso devido aos problemas 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, caímos para 100 KB, o que é aceitável.

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

Use o formato certo para conteúdo animado

GIFs podem sair muito caros. Surpreendentemente, o formato GIF nunca foi criado para ser uma plataforma de animação. Portanto, alternar para um formato de vídeo mais adequado oferece uma grande economia em termos de tamanho do 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 se mudar para um formato de vídeo mais eficiente. Nosso clipe teve um peso de cerca de 7,3 MB, muito para um site razoável.Por isso, nós o transformamos em um elemento de vídeo com dois arquivos de origem: mp4 e WebM para oferecer suporte mais amplo aos navegadores.

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

Usamos a ferramenta FFmpeg para converter nosso GIF de animação em um arquivo mp4. O formato WebM oferece economias ainda maiores: a API ImageOptim pode fazer essa conversão por você.

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

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

Ainda assim, 1 MB é um grande recurso para forçar o acesso, especialmente para usuários com largura de banda restrita. Felizmente, podemos usar a API Effective Type para perceber que a largura de banda é lenta e fornecer um JPEG muito menor.

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

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

Isso remove um pouco da 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 possa vê-las na página imediatamente.

O Lighthouse sinalizará esse comportamento na auditoria de imagens fora da tela, e também será possível vê-lo no painel de rede do DevTools. Se você vir muitas imagens sendo recebidas 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, é necessário usar o JavaScript para adicionar esse recurso. Usamos a biblioteca Lazysizes para adicionar um 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 pré-busca de modo proativo os elementos próximos à visualização para melhorar a experiência do usuário. Ele também oferece uma integração opcional do IntersectionObserver, que proporciona pesquisas de visibilidade muito eficientes.

Após essa mudança, nossas imagens serão buscadas sob demanda. Para se aprofundar nesse tópico, confira o images.guide, um recurso muito útil e abrangente.

Ajude o navegador a fornecer recursos essenciais antecipadamente

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

Algo que pode ser útil somos nós, como autores da página, informando 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, 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 de carregamento personalizado baseadas em lógica que são feitas com script.

Vamos conferir como o Lighthouse ajuda a usar alguns desses recursos com eficiência.

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

Evitar várias viagens de ida e volta caras para qualquer origem
Figura 17. Evite diversas viagens de ida e volta caras para qualquer origem.

No caso do app Oodle, na verdade, estamos usando muito o Google Fonts. Sempre que você soltar uma folha de estilo do Google Fonts na sua página, ela conectará até dois subdomínios. O que o Lighthouse diz é que, se pudéssemos aquecer essa conexão, poderíamos economizar até 300 milissegundos no tempo de conexão inicial.

Aproveitando a pré-conexão com relação de link, podemos mascarar efetivamente essa latência de conexão.

Especialmente com algo como o Google Fonts, em que nosso CSS de tipo de fonte está hospedado em googleapis.com e nossos recursos de fonte estão hospedados em Gstatic, isso pode ter um grande impacto. Aplicamos essa otimização e economizamos algumas centenas de milissegundos.

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

Pré-carregar solicitações principais
Figura 18. Pré-carregar solicitações principais

O <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.

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

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

O impacto que isso pode ter na sua página é bastante simples.

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

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

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

Essa não é tão simples se você vai tentar pré-carregar fontes usando o Google Fonts, mas há um problema.

Os URLs do Google Fonts que especificamos em nossos tipos de fontes nas folhas de estilo eram algo que a equipe de fontes atualiza regularmente. Esses URLs podem expirar ou ser atualizados com frequência regular. Portanto, o que sugerimos fazer se você quiser controle total sobre a experiência de carregamento de fontes é auto-hospedar suas fontes da Web. Isso pode ser ótimo porque dá acesso a coisas como pré-carregamento de rel de link.

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

Não importa se você usa fontes da Web como parte dos seus recursos essenciais ou se elas são JavaScript, tente ajudar o navegador a fornecer 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 o 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
Figura 20. Dicas de prioridade

Esse é um novo recurso que permite sugerir ao navegador a importância de um recurso. Ele expõe um novo atributo, a importância, com os valores baixos, altos ou automáticos.

Isso nos permite transmitir a redução da 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 nossas imagens hero.

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

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

Antes de adicionarmos o carregamento lento às nossas imagens, o que o navegador fazia era, nós tínhamos esse carrossel de imagens com todos os nossos doodles, e o navegador buscava todas as imagens no início do carrossel com alta prioridade logo no início. Infelizmente, foram as imagens no meio do carrossel que foram mais importantes para o usuário. O que fizemos foi definir a importância dessas imagens de plano de fundo para muito baixa, as de primeiro plano para muito alta, e o que isso causou foi um impacto de dois segundos sobre o 3G lento e a rapidez com que conseguimos buscar e renderizar essas imagens. Foi uma experiência positiva e positiva.

Esperamos disponibilizar esse recurso no site Canary em algumas semanas, então fique de olho.

Usar uma estratégia de carregamento de fontes da Web

A tipografia é fundamental para um bom design. Se você está usando fontes da Web, o ideal é não bloquear a renderização do texto nem mostrar textos invisíveis.

Destacamos isso no Lighthouse agora, com a auditoria evitar texto invisível enquanto as fontes da Web estão carregando.

Evitar texto invisível durante o carregamento das fontes da Web
Figura 22. Evite texto invisível enquanto as fontes da Web estão sendo carregadas

Se você carregar suas fontes da Web usando um bloco de tipos de fonte, o navegador poderá decidir o que fazer se a busca por essa fonte da Web demorar muito. Alguns navegadores esperam até três segundos por isso antes de voltar a usar uma fonte do sistema e a trocam pela fonte assim que o download é concluído.

Estamos tentando evitar esse texto invisível, então, neste caso, não teríamos conseguido ver os doodles clássicos desta semana se a fonte da Web tivesse levado muito tempo. Felizmente, com o 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 substitutas 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 renderizar o texto imediatamente com uma fonte substituta se demorar um pouco para carregar. Ele vai trocá-lo quando o tipo de fonte estiver disponível.

No caso do nosso app, o motivo disso foi que foi possível mostrar textos significativos logo no início e fazer a transição para a fonte da Web quando ela estava pronta.

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

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

Existem muitos recursos de plataforma da Web que podem ser usados para otimizar sua experiência de carregamento de fontes. Além disso, confira o repositório Web Font Recipes de Zach Leatherman, porque é ótimo.

Reduzir os scripts de bloqueio de renderização

Há outras partes do nosso aplicativo que podem ser enviadas anteriormente na cadeia de download para fornecer pelo menos uma experiência básica ao usuário um pouco mais cedo.

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

Reduza a oportunidade de folhas de estilo que bloqueiam a renderização
Figura 24. Reduza a oportunidade de folhas de estilo de bloqueio de renderização

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

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

Se os estilos responsáveis por essa renderização inicial forem extraídos e in-line no HTML, o navegador poderá renderizá-los imediatamente sem esperar a chegada das folhas de estilo externas.

No nosso caso, usamos um módulo do NPM chamado Critical para inserir in-line o conteúdo essencial em index.html durante uma etapa de build.

Embora o módulo tenha feito a maior parte do trabalho pesado para nós, ainda era um pouco complicado fazer com que ele funcionasse sem problemas em diferentes trajetos.

Se você não tiver cuidado ou se a estrutura do site for muito complexa, poderá 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 o desempenho desde o início. Se você não projetar para o desempenho desde o início, há uma grande chance de enfrentar problemas mais tarde.

No final, nosso risco valeu a pena, conseguimos fazê-lo funcionar e o app começou a entregar conteúdo muito antes, melhorando significativamente o tempo da primeira exibição significativa.

O resultado

Essa era uma longa lista de otimizações de desempenho que aplicamos ao nosso site. Vamos dar uma olhada no 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 alimentadas por verificações e análises contínuas do relatório do Lighthouse. Se você quiser conferir como implementamos tecnicamente todas as melhorias, confira nosso repositório, especialmente os PRs que chegaram lá.

Desempenho preditivo: experiências do usuário orientadas por dados

Acreditamos que o machine learning representa uma grande oportunidade para o futuro em muitas áreas. Uma ideia que esperamos gerar mais experimentos 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 acreditarmos que sim, podemos priorizar uma pequena quantidade de recursos, mas é muito difícil escaloná-la para o site inteiro.

Na verdade, temos dados disponíveis para informar melhor nossas otimizações hoje. Com a API de relatórios do Google Analytics, podemos analisar a próxima página principal e as porcentagens de saída de qualquer URL no nosso site e, assim, tirar conclusões sobre quais recursos devemos priorizar.

Se combinarmos isso com um bom modelo de probabilidade, evitaremos desperdiçar os dados do usuário fazendo a pré-busca do conteúdo de maneira agressiva. Podemos aproveitar os dados do Google Analytics e usar machine learning e modelos como as cadeias de Markov ou a rede neural para implementar esses modelos.

Pacotes orientados por dados para apps da Web
Figura 25. Pacotes orientados por dados para apps da Web

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

Guess.js
Figura 26. Guess.js

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 disso. Tudo é de código aberto e está disponível no GitHub hoje. Ele foi desenvolvido em colaboração com a comunidade de código aberto por Minko Gechev, Kyle Matthews da Gatsby, Katie Hempenius e vários outros.

Acesse Guess.js e conte o que você 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á vimos a lentidão no carregamento de páginas em qualquer lugar, mas agora temos a oportunidade de oferecer aos nossos usuários experiências mais agradáveis com carregamento rápido.

Melhorar o desempenho é 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, é possível 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.