Como alterar a URL sem recarregar a página: otimização e desempenho

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que alterar a URL sem recarregar é um superpoder

\n\n

Imagine: você abre um serviço web moderno — por exemplo, um cliente de e-mail ou um editor de documentos online. Você alterna entre pastas, abre e-mails, muda as configurações — e a página não recarrega. Enquanto isso, a barra de endereços do navegador muda, os botões "Voltar" e "Avançar" funcionam, e os favoritos salvam o estado desejado. Como isso funciona? A magia da modificação da URL sem recarregar a página.

\n\n

Isso não é apenas um "recurso interessante" — é a base de aplicações de página única (SPA) de alto desempenho. Antigamente, os desenvolvedores usavam hashes (#), mas isso era feio e inconveniente. Hoje, temos a History API — uma ferramenta poderosa que permite alterar a URL instantaneamente, sem cintilação, sem perda de estado, sem requisições HTTP desnecessárias.

\n\n

Neste artigo, vamos explorar como funcionam pushState e replaceState, como eles afetam o desempenho e como evitar armadilhas na otimização. Vamos nessa!

\n\n

Fundamentos: History API e seus métodos

\n\n

A especificação HTML5 nos presenteou com a interface window.history. Os dois principais métodos para alterar a URL sem recarregar são pushState e replaceState.

\n\n

pushState — adiciona uma nova entrada no histórico

\n\n

pushState cria uma nova entrada no histórico do navegador. O usuário pode clicar em "Voltar" e retornar à URL anterior.

\n\n
// Sintaxe\nhistory.pushState(state, title, url);\n\n// Exemplo\nhistory.pushState({ page: 1 }, 'Página Inicial', '/home');\n
\n\n

Parâmetros:

\n
    \n
  • state — objeto de dados associado a esta entrada. Pode armazenar qualquer dado serializável (por exemplo, estado do filtro, ID do elemento).
  • \n
  • title — título da página (a maioria dos navegadores ignora, mas é melhor passar uma string vazia ou um texto significativo).
  • \n
  • url — nova URL. Deve ser relativa ou absoluta, mas dentro da mesma origem (protocolo + domínio + porta).
  • \n
\n\n

replaceState — substitui a entrada atual

\n\n

replaceState modifica a entrada atual no histórico, sem criar uma nova. O usuário não conseguirá voltar "para trás" para a URL anterior — ela será substituída.

\n\n
// Sintaxe\nhistory.replaceState(state, title, url);\n\n// Exemplo — atualiza a URL ao alterar o filtro\nfunction applyFilter(filterName) {\n  history.replaceState({ filter: filterName }, '', `?filter=${filterName}`);\n  updateUI(filterName);\n}\n
\n\n

popstate — ouvindo as mudanças no histórico

\n\n

Quando o usuário clica em "Voltar" ou "Avançar", o navegador gera o evento popstate. Importante: pushState e replaceState não disparam este evento — ele precisa ser tratado separadamente.

\n\n
window.addEventListener('popstate', function(event) {\n  if (event.state) {\n    // Restaura o estado com base nos dados de event.state\n    restorePageState(event.state);\n  } else {\n    // Se o state estiver vazio — talvez o usuário saiu dos limites da nossa aplicação\n    loadDefaultState();\n  }\n});\n
\n\n

Otimização de desempenho: o que é importante saber

\n\n

A modificação da URL é rápida. Mas existem nuances que podem transformar um superpoder em uma fonte de bugs.

\n\n

1. Não chame pushState com muita frequência

\n\n

Cada chamada de pushState adiciona uma entrada no histórico. Se você chamá-lo a cada movimento do mouse ou a cada caractere digitado na busca — o histórico ficará inflado, e o usuário terá que clicar em "Voltar" 100 vezes para sair do filtro. Use replaceState para estados intermediários e pushState para ações logicamente concluídas (por exemplo, abrir uma nova página).

\n\n

2. Sincronize o estado corretamente

\n\n

Ao usar pushState ou replaceState, certifique-se de que o estado da interface do usuário corresponda exatamente à URL. Caso contrário, ao pressionar "Voltar", o usuário verá uma URL, mas o conteúdo errado. Armazene dados suficientes no parâmetro state para restaurar completamente a página.

\n\n

3. Evite duplicação de dados

\n\n

Não armazene no state informações que já estão na URL (por exemplo, parâmetros de consulta). Isso leva à duplicação e a possíveis inconsistências. Use o state para dados que não podem ser expressos na URL (por exemplo, posição de rolagem, estado de modais abertos).

\n\n

4. Considere o carregamento lento (lazy loading)

\n\n

Se a alteração da URL exigir o carregamento de novos dados (por exemplo, via API), implemente o carregamento lento. Não bloqueie a interface do usuário enquanto os dados estão sendo carregados. Mostre um indicador de carregamento e atualize o conteúdo após receber a resposta.

\n\n

Exemplos práticos de uso

\n\n

Exemplo 1: Navegação por abas sem recarregar

\n\n

Suponha que você tenha um painel com abas: "Visão Geral", "Analytics", "Configurações". Ao clicar em uma aba, a URL muda, mas a página não recarrega.

\n\n
function switchTab(tabName) {\n  // Atualiza a URL\n  history.pushState({ tab: tabName }, '', `/dashboard/${tabName}`);\n  \n  // Atualiza a interface\n  document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');\n  document.getElementById(`tab-${tabName}`).style.display = 'block';\n  \n  // Atualiza o título da página\n  document.title = `Painel - ${tabName}`;\n}\n\n// Manipulando o botão "Voltar"\nwindow.addEventListener('popstate', function(event) {\n  if (event.state && event.state.tab) {\n    switchTab(event.state.tab);\n  }\n});\n
\n\n

Exemplo 2: Filtragem de produtos com replaceState

\n\n

Ao aplicar filtros em uma loja online, use replaceState para não poluir o histórico com cada pequena alteração.

\n\n
function updateFilters(filterType, value) {\n  // Obtém os filtros atuais da URL\n  const params = new URLSearchParams(window.location.search);\n  params.set(filterType, value);\n  \n  // Substitui a URL sem criar uma nova entrada no histórico\n  history.replaceState({ filters: params.toString() }, '', `?${params.toString()}`);\n  \n  // Carrega os produtos com os novos filtros\n  loadProducts(params.toString());\n}\n
\n\n

Conclusão: Quando usar pushState e replaceState

\n\n

A capacidade de alterar a URL sem recarregar a página tornou-se um padrão no desenvolvimento web moderno. Use pushState para ações significativas do usuário que merecem uma entrada separada no histórico (navegação entre páginas, abertura de itens). Use replaceState para estados intermediários e atualizações que não devem criar entradas extras no histórico (filtros, parâmetros de busca, alternância de modos de visualização).

\n\n

Lembre-se: a History API é uma ferramenta poderosa, mas exige uma abordagem cuidadosa. Sincronize o estado, evite chamadas excessivas e sempre teste o comportamento dos botões "Voltar" e "Avançar". Com a abordagem correta, seus usuários terão uma experiência suave e intuitiva, como em aplicações nativas.

\n\n

Boa codificação!

Blogs

Book Recommendations