Enviando Dados em Background com a Beacon API

A navegação entre páginas não deveria ser refém de requisições de analytics. Quando os usuários clicam em um link ou fecham uma aba, as requisições HTTP tradicionais podem bloquear ou falhar completamente, deixando você com dados incompletos e usuários frustrados. A solução da Beacon API muda isso ao enfileirar requisições em background, garantindo que seus dados cheguem ao servidor sem impactar a performance.
Este artigo demonstra como implementar navigator.sendBeacon()
para transmissão confiável de dados em background. Você aprenderá aplicações práticas como rastreamento de analytics e relatórios de erro, entenderá por que ela supera fetch()
durante transições de página, e verá uma implementação completa usando JavaScript moderno.
Principais Pontos
- A Beacon API enfileira requisições em background sem bloquear a navegação da página
- Use
navigator.sendBeacon()
para analytics, relatórios de erro e rastreamento de ações do usuário - Beacons são mais confiáveis que
fetch()
durante eventos de descarregamento de página - Mantenha payloads pequenos e agrupe múltiplos eventos para performance otimizada
- Implemente fallbacks para navegadores mais antigos que não suportam a Beacon API
- A API retorna um boolean indicando enfileiramento bem-sucedido, não confirmação de entrega
O que Torna a Beacon API Diferente
A abordagem da Beacon API resolve um problema fundamental: requisições HTTP tradicionais bloqueiam a navegação da página. Quando você usa fetch()
ou XMLHttpRequest
durante eventos de unload, o navegador deve aguardar a conclusão da requisição antes de permitir que a navegação prossiga. Isso cria uma experiência ruim para o usuário e transmissão de dados não confiável.
A Beacon API opera de forma diferente. Ela enfileira requisições de forma assíncrona e gerencia a transmissão em background, mesmo após a página ter sido fechada. O navegador gerencia todo o processo sem bloquear a navegação ou exigir que seu JavaScript permaneça ativo.
Por que Métodos Tradicionais Falham Durante Transições de Página
Considere este cenário comum usando fetch()
:
window.addEventListener('beforeunload', () => {
// Isso pode falhar ou bloquear a navegação
fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ event: 'page_exit' })
})
})
Esta abordagem tem vários problemas:
- A requisição pode ser cancelada quando a página descarrega
- A navegação é bloqueada até a requisição completar
- Não há garantia de que os dados chegarão ao servidor
- Experiência ruim do usuário com transições de página atrasadas
Como navigator.sendBeacon() Funciona
O método navigator.sendBeacon()
aceita dois parâmetros: um endpoint de URL e dados opcionais. Ele retorna um boolean indicando se a requisição foi enfileirada com sucesso (não se chegou ao servidor).
const success = navigator.sendBeacon(url, data)
O navegador gerencia a transmissão real, otimizando para condições de rede e recursos do sistema. Esta abordagem “disparar e esquecer” é perfeita para analytics, logging e dados de diagnóstico onde você não precisa de uma resposta.
Exemplos de Implementação Prática
Rastreamento Básico de Analytics
Aqui está uma implementação mínima para rastrear sessões de usuário:
// Rastrear dados de sessão
const sessionData = {
userId: 'user123',
sessionDuration: Date.now() - sessionStart,
pageViews: pageViewCount,
timestamp: new Date().toISOString()
}
// Enviar dados quando a página fica oculta ou o usuário navega para longe
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(sessionData)
navigator.sendBeacon('/log', payload)
}
})
window.addEventListener('beforeunload', () => {
const payload = JSON.stringify(sessionData)
navigator.sendBeacon('/log', payload)
})
Sistema de Relatório de Erros
A Beacon API se destaca em capturar e relatar erros JavaScript:
window.addEventListener('error', (event) => {
const errorData = {
message: event.message,
filename: event.filename,
line: event.lineno,
column: event.colno,
stack: event.error?.stack,
userAgent: navigator.userAgent,
timestamp: Date.now()
}
if (navigator.sendBeacon) {
navigator.sendBeacon('/log', JSON.stringify(errorData))
}
})
Rastreamento de Ações do Usuário
Rastreie interações específicas do usuário sem bloquear a interface:
function trackUserAction(action, details) {
const actionData = {
action,
details,
timestamp: Date.now(),
url: window.location.href
}
if (navigator.sendBeacon) {
navigator.sendBeacon('/log', JSON.stringify(actionData))
}
}
// Exemplos de uso
document.getElementById('cta-button').addEventListener('click', () => {
trackUserAction('cta_click', { buttonId: 'cta-button' })
})
document.addEventListener('scroll', throttle(() => {
const scrollPercent = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
)
trackUserAction('scroll', { percentage: scrollPercent })
}, 1000))
Implementação Completa de Analytics
Aqui está um exemplo abrangente combinando múltiplos cenários de rastreamento:
class AnalyticsTracker {
constructor(endpoint = '/log') {
this.endpoint = endpoint
this.sessionStart = Date.now()
this.events = []
this.setupEventListeners()
}
setupEventListeners() {
// Rastrear mudanças de visibilidade da página
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.sendBatch()
}
})
// Rastrear descarregamento da página
window.addEventListener('beforeunload', () => {
this.sendBatch()
})
// Rastrear erros
window.addEventListener('error', (event) => {
this.trackEvent('error', {
message: event.message,
filename: event.filename,
line: event.lineno
})
})
}
trackEvent(type, data) {
this.events.push({
type,
data,
timestamp: Date.now()
})
// Enviar lote se tivermos muitos eventos
if (this.events.length >= 10) {
this.sendBatch()
}
}
sendBatch() {
if (this.events.length === 0) return
const payload = {
sessionId: this.generateSessionId(),
sessionDuration: Date.now() - this.sessionStart,
events: this.events,
url: window.location.href,
userAgent: navigator.userAgent
}
if (navigator.sendBeacon) {
const success = navigator.sendBeacon(
this.endpoint,
JSON.stringify(payload)
)
if (success) {
this.events = [] // Limpar eventos enviados
}
}
}
generateSessionId() {
return Math.random().toString(36).substring(2, 15)
}
}
// Inicializar rastreador
const tracker = new AnalyticsTracker('/log')
// Rastrear eventos customizados
tracker.trackEvent('page_view', { page: window.location.pathname })
Suporte de Navegadores e Fallbacks
A Beacon API tem excelente suporte em navegadores modernos. Para navegadores mais antigos, implemente um fallback gracioso:
function sendData(url, data) {
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, data)
}
// Fallback para navegadores mais antigos
try {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, false) // Síncrono para eventos de unload
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(data)
return true
} catch (error) {
console.warn('Falha ao enviar dados:', error)
return false
}
}
Melhores Práticas para Implementação de Beacon
Mantenha Payloads Pequenos
A Beacon API é projetada para pequenos pacotes de dados. Limite payloads a informações essenciais:
// Bom: Dados focados e essenciais
const essentialData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now()
}
// Evite: Dados grandes e desnecessários
const bloatedData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now(),
entireDOMState: document.documentElement.outerHTML,
allCookies: document.cookie,
completeUserHistory: getUserHistory()
}
Agrupe Múltiplos Eventos
Em vez de enviar beacons individuais para cada evento, agrupe-os:
const eventBatch = []
function addEvent(eventData) {
eventBatch.push(eventData)
// Enviar lote quando atingir um certo tamanho
if (eventBatch.length >= 5) {
sendBatch()
}
}
function sendBatch() {
if (eventBatch.length > 0) {
navigator.sendBeacon('/log', JSON.stringify(eventBatch))
eventBatch.length = 0 // Limpar o lote
}
}
Gerencie Falhas de Rede de Forma Graciosa
Como beacons não fornecem feedback de resposta, implemente validação do lado do cliente:
function validateAndSend(data) {
// Validar estrutura de dados
if (!data || typeof data !== 'object') {
console.warn('Dados inválidos para beacon')
return false
}
// Verificar tamanho do payload (navegadores tipicamente limitam a 64KB)
const payload = JSON.stringify(data)
if (payload.length > 65536) {
console.warn('Payload muito grande para beacon')
return false
}
return navigator.sendBeacon('/log', payload)
}
Beacon API vs Fetch Durante Transições de Página
A diferença principal fica aparente durante eventos de descarregamento de página:
// Beacon API: Não-bloqueante, confiável
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('/log', JSON.stringify(data)) // ✅ Confiável
})
// Fetch API: Pode bloquear ou falhar
window.addEventListener('beforeunload', () => {
fetch('/log', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true // Ajuda mas não garante sucesso
}) // ❌ Pode falhar durante navegação
})
A flag keepalive
em requisições fetch fornece funcionalidade similar, mas com menos confiabilidade que a Beacon API, que foi especificamente projetada para este caso de uso.
Conclusão
A Beacon API fornece uma solução robusta para transmissão de dados em background sem bloquear a navegação da página. Ao enfileirar requisições de forma assíncrona, ela garante entrega confiável de dados enquanto mantém uma experiência de usuário otimizada. Seja implementando rastreamento de analytics, relatórios de erro ou logging de ações do usuário, navigator.sendBeacon()
oferece a confiabilidade e performance que métodos HTTP tradicionais não conseguem igualar durante transições de página.
A natureza “disparar e esquecer” dos beacons os torna ideais para cenários onde você precisa enviar dados mas não requer uma resposta. Combinado com estratégias adequadas de agrupamento e otimização de payload, a Beacon API se torna uma ferramenta essencial para aplicações web modernas que priorizam tanto coleta de dados quanto experiência do usuário.
FAQs
A Beacon API é um padrão web JavaScript que envia pequenas quantidades de dados para um servidor sem aguardar uma resposta. Diferente de fetch ou XMLHttpRequest, requisições beacon são enfileiradas pelo navegador e enviadas de forma assíncrona, tornando-as perfeitas para analytics e logging durante eventos de navegação de página onde requisições tradicionais podem falhar ou bloquear a experiência do usuário.
Não, a Beacon API é projetada para pequenos pacotes de dados, tipicamente limitados a 64KB pela maioria dos navegadores. Se você precisa enviar grandes quantidades de dados, considere agrupar requisições menores ou usar métodos alternativos como a Fetch API com a flag keepalive para cenários de timing não crítico.
A Beacon API tem excelente suporte em todos os navegadores modernos incluindo Chrome, Firefox, Safari e Edge. Ela não funciona no Internet Explorer. Para suporte a navegadores mais antigos, implemente um fallback usando XMLHttpRequest ou fetch, embora essas alternativas possam não fornecer a mesma confiabilidade durante eventos de descarregamento de página.
O método navigator.sendBeacon retorna um boolean indicando se a requisição foi enfileirada com sucesso pelo navegador, não se chegou ao servidor. Como beacons são disparar-e-esquecer, você não pode acessar a resposta do servidor ou saber definitivamente se os dados foram recebidos. Isso é por design para performance otimizada.
Use a Beacon API quando precisar enviar dados durante transições de página, eventos de unload, ou quando a página fica oculta, e você não precisa de uma resposta. Use fetch para chamadas de API regulares onde você precisa lidar com respostas, erros, ou quando o timing não é crítico para a navegação da página.