Envoi de données en arrière-plan avec l'API Beacon

La navigation sur une page ne devrait pas être prise en otage par les requêtes d’analyse. Lorsque les utilisateurs cliquent sur un lien ou ferment un onglet, les requêtes HTTP traditionnelles peuvent bloquer ou échouer complètement, vous laissant avec des données incomplètes et des utilisateurs frustrés. La solution de l’API Beacon change cela en mettant les requêtes en file d’attente en arrière-plan, garantissant que vos données atteignent le serveur sans impacter les performances.
Cet article démontre comment implémenter navigator.sendBeacon()
pour une transmission fiable de données en arrière-plan. Vous apprendrez les applications pratiques comme le suivi d’analyse et le rapport d’erreurs, comprendrez pourquoi elle surpasse fetch()
lors des transitions de page, et verrez une implémentation complète utilisant du JavaScript moderne.
Points clés à retenir
- L’API Beacon met les requêtes en file d’attente en arrière-plan sans bloquer la navigation
- Utilisez
navigator.sendBeacon()
pour l’analyse, le rapport d’erreurs et le suivi d’actions utilisateur - Les beacons sont plus fiables que
fetch()
lors des événements de déchargement de page - Gardez les charges utiles petites et groupez plusieurs événements pour des performances optimales
- Implémentez des solutions de repli pour les navigateurs plus anciens qui ne supportent pas l’API Beacon
- L’API retourne un booléen indiquant la mise en file d’attente réussie, pas la confirmation de livraison
Ce qui rend l’API Beacon différente
L’approche de l’API Beacon résout un problème fondamental : les requêtes HTTP traditionnelles bloquent la navigation. Lorsque vous utilisez fetch()
ou XMLHttpRequest
lors d’événements de déchargement, le navigateur doit attendre que la requête se termine avant de permettre à la navigation de continuer. Cela crée une mauvaise expérience utilisateur et une transmission de données peu fiable.
L’API Beacon fonctionne différemment. Elle met les requêtes en file d’attente de manière asynchrone et gère la transmission en arrière-plan, même après que la page ait été fermée. Le navigateur gère l’ensemble du processus sans bloquer la navigation ou nécessiter que votre JavaScript reste actif.
Pourquoi les méthodes traditionnelles échouent lors des transitions de page
Considérez ce scénario courant utilisant fetch()
:
window.addEventListener('beforeunload', () => {
// Ceci peut échouer ou bloquer la navigation
fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ event: 'page_exit' })
})
})
Cette approche présente plusieurs problèmes :
- La requête peut être annulée lorsque la page se décharge
- La navigation est bloquée jusqu’à ce que la requête se termine
- Aucune garantie que les données atteindront le serveur
- Mauvaise expérience utilisateur avec des transitions de page retardées
Comment fonctionne navigator.sendBeacon()
La méthode navigator.sendBeacon()
accepte deux paramètres : une URL de point de terminaison et des données optionnelles. Elle retourne un booléen indiquant si la requête a été mise en file d’attente avec succès (pas si elle a atteint le serveur).
const success = navigator.sendBeacon(url, data)
Le navigateur gère la transmission réelle, optimisant pour les conditions réseau et les ressources système. Cette approche “fire-and-forget” est parfaite pour l’analyse, la journalisation et les données de diagnostic où vous n’avez pas besoin de réponse.
Exemples d’implémentation pratique
Suivi d’analyse de base
Voici une implémentation minimale pour suivre les sessions utilisateur :
// Suivre les données de session
const sessionData = {
userId: 'user123',
sessionDuration: Date.now() - sessionStart,
pageViews: pageViewCount,
timestamp: new Date().toISOString()
}
// Envoyer les données quand la page devient cachée ou que l'utilisateur navigue ailleurs
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)
})
Système de rapport d’erreurs
L’API Beacon excelle dans la capture et le rapport d’erreurs 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))
}
})
Suivi d’actions utilisateur
Suivez les interactions utilisateur spécifiques sans bloquer l’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))
}
}
// Exemples d'utilisation
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))
Implémentation complète d’analyse
Voici un exemple complet combinant plusieurs scénarios de suivi :
class AnalyticsTracker {
constructor(endpoint = '/log') {
this.endpoint = endpoint
this.sessionStart = Date.now()
this.events = []
this.setupEventListeners()
}
setupEventListeners() {
// Suivre les changements de visibilité de page
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.sendBatch()
}
})
// Suivre le déchargement de page
window.addEventListener('beforeunload', () => {
this.sendBatch()
})
// Suivre les erreurs
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()
})
// Envoyer le lot si nous avons trop d'événements
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 = [] // Vider les événements envoyés
}
}
}
generateSessionId() {
return Math.random().toString(36).substring(2, 15)
}
}
// Initialiser le tracker
const tracker = new AnalyticsTracker('/log')
// Suivre les événements personnalisés
tracker.trackEvent('page_view', { page: window.location.pathname })
Support navigateur et solutions de repli
L’API Beacon a un excellent support dans les navigateurs modernes. Pour les navigateurs plus anciens, implémentez une solution de repli élégante :
function sendData(url, data) {
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, data)
}
// Solution de repli pour les navigateurs plus anciens
try {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, false) // Synchrone pour les événements de déchargement
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(data)
return true
} catch (error) {
console.warn('Échec de l\'envoi des données:', error)
return false
}
}
Bonnes pratiques pour l’implémentation Beacon
Gardez les charges utiles petites
L’API Beacon est conçue pour de petits paquets de données. Limitez les charges utiles aux informations essentielles :
// Bien : Données focalisées et essentielles
const essentialData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now()
}
// À éviter : Données volumineuses et inutiles
const bloatedData = {
event: 'conversion',
value: 29.99,
timestamp: Date.now(),
entireDOMState: document.documentElement.outerHTML,
allCookies: document.cookie,
completeUserHistory: getUserHistory()
}
Groupez plusieurs événements
Au lieu d’envoyer des beacons individuels pour chaque événement, groupez-les ensemble :
const eventBatch = []
function addEvent(eventData) {
eventBatch.push(eventData)
// Envoyer le lot quand il atteint une certaine taille
if (eventBatch.length >= 5) {
sendBatch()
}
}
function sendBatch() {
if (eventBatch.length > 0) {
navigator.sendBeacon('/log', JSON.stringify(eventBatch))
eventBatch.length = 0 // Vider le lot
}
}
Gérez les échecs réseau avec élégance
Puisque les beacons ne fournissent pas de retour de réponse, implémentez une validation côté client :
function validateAndSend(data) {
// Valider la structure des données
if (!data || typeof data !== 'object') {
console.warn('Données invalides pour le beacon')
return false
}
// Vérifier la taille de la charge utile (les navigateurs limitent généralement à 64KB)
const payload = JSON.stringify(data)
if (payload.length > 65536) {
console.warn('Charge utile trop volumineuse pour le beacon')
return false
}
return navigator.sendBeacon('/log', payload)
}
API Beacon vs Fetch lors des transitions de page
La différence clé devient apparente lors des événements de déchargement de page :
// API Beacon : Non-bloquante, fiable
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('/log', JSON.stringify(data)) // ✅ Fiable
})
// API Fetch : Peut bloquer ou échouer
window.addEventListener('beforeunload', () => {
fetch('/log', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true // Aide mais ne garantit pas le succès
}) // ❌ Peut échouer lors de la navigation
})
Le flag keepalive
dans les requêtes fetch fournit une fonctionnalité similaire mais avec moins de fiabilité que l’API Beacon, qui a été spécifiquement conçue pour ce cas d’usage.
Conclusion
L’API Beacon fournit une solution robuste pour la transmission de données en arrière-plan sans bloquer la navigation. En mettant les requêtes en file d’attente de manière asynchrone, elle garantit une livraison fiable des données tout en maintenant une expérience utilisateur optimale. Que vous implémentiez le suivi d’analyse, le rapport d’erreurs ou la journalisation d’actions utilisateur, navigator.sendBeacon()
offre la fiabilité et les performances que les méthodes HTTP traditionnelles ne peuvent pas égaler lors des transitions de page.
La nature “fire-and-forget” des beacons les rend idéaux pour les scénarios où vous devez envoyer des données mais n’avez pas besoin de réponse. Combinée avec des stratégies de groupement appropriées et une optimisation de la charge utile, l’API Beacon devient un outil essentiel pour les applications web modernes qui priorisent à la fois la collecte de données et l’expérience utilisateur.
FAQ
L'API Beacon est un standard web JavaScript qui envoie de petites quantités de données à un serveur sans attendre de réponse. Contrairement à fetch ou XMLHttpRequest, les requêtes beacon sont mises en file d'attente par le navigateur et envoyées de manière asynchrone, les rendant parfaites pour l'analyse et la journalisation lors d'événements de navigation où les requêtes traditionnelles pourraient échouer ou bloquer l'expérience utilisateur.
Non, l'API Beacon est conçue pour de petits paquets de données, généralement limités à 64KB par la plupart des navigateurs. Si vous devez envoyer de plus grandes quantités de données, considérez le groupement de requêtes plus petites ou l'utilisation de méthodes alternatives comme l'API Fetch avec le flag keepalive pour les scénarios de timing non critiques.
L'API Beacon a un excellent support dans tous les navigateurs modernes incluant Chrome, Firefox, Safari et Edge. Elle ne fonctionne pas dans Internet Explorer. Pour le support des navigateurs plus anciens, implémentez une solution de repli utilisant XMLHttpRequest ou fetch, bien que ces alternatives ne puissent pas fournir la même fiabilité lors des événements de déchargement de page.
La méthode navigator.sendBeacon retourne un booléen indiquant si la requête a été mise en file d'attente avec succès par le navigateur, pas si elle a atteint le serveur. Puisque les beacons sont fire-and-forget, vous ne pouvez pas accéder à la réponse du serveur ou savoir définitivement si les données ont été reçues. C'est par conception pour des performances optimales.
Utilisez l'API Beacon quand vous devez envoyer des données lors de transitions de page, d'événements de déchargement, ou quand la page devient cachée, et que vous n'avez pas besoin de réponse. Utilisez fetch pour les appels API réguliers où vous devez gérer les réponses, les erreurs, ou quand le timing n'est pas critique pour la navigation.