Blog/Frontend

Como otimizar a performance de aplicações web frontend

Frontend moderno não se trata apenas de usar poderosos frameworks, mas também de saber como otimizar os recursos para uma boa performance.

Publicado em

Atualizado em

Palavras-chave:frontend, otimização, SPA, Single Page Applications, HTML, CSS, JS

Como otimizar a performance de aplicações web frontend

Teoricamente, os frameworks de frontend modernos foram projetados para maior performance no desenvolvimento de aplicações web em vários sentidos, desde o funcionamento da aplicação até o processo de desenvolvimento em si.

Entre os recursos interessantes dos frameworks SPA, podemos citar por exemplo:

  • Navegação roteada do lado cliente (navegador) por History API, reaproveitando todo o HTML/CSS/JS base, sem a necessidade de requisitá-lo todo novamente ao servidor a cada página
  • Renderização condicional de conteúdo dinamicamente processado no lado cliente, requisitando apenas dados a uma API para isso
  • Composição de páginas com reutilização de componentes e módulos

Outras características poderiam ser citadas como vantagens, mas neste artigo eu venho focar nos pontos que merecem mais atenção e cuidado para não se tornarem problemas e prejudicarem a performance do seu site.

Se você leu meu artigo anterior onde eu falo sobre SEO para Single Page Applications, viu que falei sobre a importância da performance. Neste artigo eu desenvolvo o assunto de forma um pouco mais técnica. Mas caso não tenha lido o artigo anterior, sem problemas, fique à vontade para ler se quiser.

O lado sombrio da força

Como é que algo feito para trazer grandes vantagens pode "mudar de lado" e causar problemas no final? (Já viu uma história assim?)

Mas pode! Dependendo da maneira como o código de seu frontend for desenvolvido, o "pacote" final que é publicado na web pode ter sérios problemas de performance e o que era para ser incrível pode perder muito em SEO e na experiência do usuário final.

Geralmente o uso de recursos como pré-render e SSR são usados na verdade para "contornar" os problemas de uma SPA mal desenvolvida ou de uma aplicação que na verdade nem deveria ser uma SPA, conforme já comentei no artigo anterior.

O que é uma SPA "mal desenvolvida"?

Talvez eu tenha usado uma expressão um pouco forte aqui, mas o que quero dizer é uma aplicação desenvolvida sem grandes preocupações com boas práticas e sem o uso de técnicas avançadas para otimização do código final, como code splitting e lazy loading, por exemplo.

Mas ironicamente, alguns dos maiores "vilões" nesse ponto são exatamente aqueles vistos por muitos como "heróis" no desenvolvimento frontend moderno: libs JS e CSS.

Essas libs geralmente são usadas para um desenvolvimento mais rápido e padronizado, para reutilizar recursos e estilos, diminuindo a quantidade de código a ser escrito para fazer as coisas funcionarem como esperado.

Mas... Falando de performance, não se trata apenas de escrever menos código e sim de ter menos código compilado no final. Ou seja, escrever menos código, usando bibliotecas no frontend, não necessariamente significa menos código na aplicação final. Infelizmente, muitas vezes é exatamente o contrário.

Vejo muitos desenvolvedores usarem bibliotecas em seus códigos para fazer coisas que às vezes um pouco de código nativo seria suficiente. Muito disso é falta de conhecimento dos recursos nativos, um problema antigo na verdade...

É muito comum desenvolvedores aprenderem a usar bibliotecas e frameworks antes de dominarem as tecnologias de que são feitos, caindo neles apenas com o básico necessário para começar a usá-los. Eu mesmo dominei a jQuery antes de aprender o JavaScript de verdade (por volta de 2010 a 2014).

Além disso, muitas bibliotecas também não são otimizadas, sendo escritas por desenvolvedores que fazem o mesmo, importando outras bibliotecas em seus códigos, nem sempre realmente necessárias. Com isso, temos muita "concatenação desnecessária" de código, para fazer coisas que podem ser simples quando se domina os recursos nativos da linguagem.

Outro grande problema é querer manter compatibilidade com versões mais antigas dos navegadores. Isso leva o código final a ser "compilado" com grandes quantidades de pollyfills para simular recursos que versões mais antigas dos browsers não tem. E isso provavelmente também é desnecessário atualmente, já que a grande maioria dos usuários recebe atualizações automáticas dos navegadores constantemente em seus dispositivos.

É por isso que Vite, o mais novo bundler do ecossistema Vue, chegou com a proposta de abandonar o suporte a navegadores antigos e com isso também conseguiu abandonar o já legado Webpack e seus bundles enormes e cheios de pollyfills. Vite processa seus bundles com Rollup e esbuild, focando apenas nas versões mais recentes dos navegadores que já suportam o import nativo de módulos JS. E com isso também consegue ser um bundler agnóstico a frameworks, pois além de Vue pode ser usado com React, Preact, Svelte e outros. Mas isso é assunto para outro artigo...

Como chegar a uma otimização do código na prática?

Além dos pontos já citados, eu tenho mais algumas opiniões um pouco "avançadas demais para o meu tempo" nesse ponto, mas vou tentar compartilhar aqui algumas dicas práticas e diretas que podem ajudar alguns desenvolvedores.

Alguns exemplos abaixo incluem código nativo compatível com os browsers mais recentes e com Node.js a partir da versão 14, que era a LTS mais recente na data de publicação deste artigo. Se sua aplicação é publicada com menos do que isso, então está desatualizada.

  1. Domine o JS e evite usar bibliotecas sempre que possível, fazendo melhor uso do código nativo.
// Formatação de data usando uma lib
import dayjs from 'dayjs'
import 'dayjs/locale/pt-br'

dayjs.locale('pt-br')
const format = dayjs().format('DD/MM/YYYY')

/* --- */

// Sem lib, código JS nativo
const date = new Date()
const format = date.toLocaleDateString('pt-br', {
	day: '2-digit',
	month: '2-digit',
	year: 'numeric',
})
  1. Quando precisar importar algo, prefira sempre o que pode ser feito com tree shaking, incluindo na "compilação" do seu código final apenas o que for realmente necessário. Se a biblioteca em questão não suporta este recurso, troque por outra ou por código nativo.
// Sem tree shaking
import _ from 'lodash'
const value = _.get(anObject, 'path.to.a.key', null)

/* --- */

// Com tree shaking, importe da lib apenas o que precisa
import { get } from 'lodash'
const value = get(anObject, 'path.to.a.key', null)
  1. Domine HTML/CSS e evite escrever estruturas desnecessariamente grandes. Escreva um HTML menor, com menos tags e melhor estilizado com CSS bem escrito.

Como consequência desta, eu ainda recomendo a próxima dica, que provavelmente será bem polêmica:

  1. Abandone bibliotecas CSS prontas e construa seus próprios componentes CSS. Muitas vezes, é possível fazer o mesmo que é encontrado nessas bibliotecas com menos código, pelos mesmos motivos já comentados neste artigo.

Estas 2 últimas dicas já são um pouco mais avançadas e exigiriam mais código para um exemplo. Porém, para simplificar isso, uma boa dica é procurar novos frameworks CSS, que já ultrapassaram os limites dos padrões antigos mais conhecidos e que são modulares, utilitários ou funcionais, dando a mesma possibilidade de otimização do CSS final, assim como é possível no JS.

Inclusive, sobre isso, escrevi um artigo que com certeza vai lhe interessar:

Como desenvolver boas aplicações web sem escrever CSS?

Espero que essas dicas tenham ajudado na introdução ao tema de performance no frontend.

Até mais!