Protéger l’état des applications en ligne quand les onglets se mettent en veille

Les applications web modernes ne s’exécutent plus dans un environnement stable et prévisible. Un onglet peut passer en arrière-plan, être fortement ralenti, gelé, ou même supprimé par le navigateur pour économiser de la mémoire et du CPU. Dans ce contexte, il est risqué de supposer que le JavaScript continuera à tourner normalement jusqu’au prochain clic utilisateur.

Pour les équipes produit et les développeurs, la vraie question n’est donc pas seulement « comment détecter qu’un onglet est caché ? », mais surtout « comment préserver l’état de l’application sans dépendre d’un exécution continue ? ». C’est un sujet critique pour les formulaires, les outils métiers, les interfaces temps réel et les web apps à forte interaction.

Comprendre les états d’un onglet en arrière-plan

Quand l’utilisateur change d’onglet, minimise la fenêtre ou masque complètement le document, la Page Visibility API déclenche visibilitychange. C’est le signal minimal et standard pour savoir qu’une application n’est plus visible. MDN rappelle que c’est l’outil de base pour adapter le comportement d’une page aux contraintes d’arrière-plan.

Mais la visibilité n’est qu’une première étape. Un onglet caché peut ensuite être gelé (frozen), auquel cas les tâches « freezable » sont suspendues jusqu’au dégel. Sur certains navigateurs, l’onglet peut aussi être « discarded », puis rechargé plus tard. Autrement dit, l’application doit considérer qu’un état caché peut rapidement devenir un état de fin de session probable.

Les navigateurs appliquent par ailleurs des politiques de limitation du travail en arrière-plan. Le “budget-based background timeout throttling”, introduit historiquement dans Chrome, réduit le CPU accordé aux timers de fond. Même sans gel explicite, les minuteries et callbacks ne sont donc pas fiables pour maintenir un état synchronisé en continu.

Sauvegarder au bon moment : l’état hidden comme point de bascule

Le moment le plus important pour persister l’état non sauvegardé est généralement le passage à hidden. Chrome recommande de traiter cette transition comme la dernière occasion fiable d’enregistrer les données importantes, en particulier sur mobile où l’application peut être interrompue plus brutalement.

Concrètement, cela signifie qu’il faut sauvegarder l’état applicatif dès que la page devient cachée, puis arrêter les tâches inutiles en arrière-plan. Les animations, mises à jour visuelles, sondages réseau et traitements non essentiels doivent être mis en pause rapidement, car ils consomment des ressources sans bénéfice utilisateur immédiat.

Cette stratégie change la conception du front-end : on ne cherche plus à “tenir” pendant l’absence de l’utilisateur, mais à se mettre en sécurité au plus vite. Pour des produits métiers, c’est souvent la différence entre une interface robuste et une perte de saisie difficile à expliquer au support.

Pourquoi freeze et resume doivent faire partie du design

Le passage à frozen est un moment encore plus strict. Les tâches freezable sont suspendues, et le dégel peut ne jamais avoir lieu. Chrome recommande alors de fermer proprement les connexions IndexedDB, BroadcastChannel, WebRTC, WebSocket, les sondages réseau et les Web Locks avant la mise en veille profonde.

Ce point est essentiel car le gel ne concerne pas seulement le cache arrière/avant (bfcache). Les événements freeze et resume peuvent aussi être déclenchés lorsqu’un onglet en arrière-plan est gelé pour réduire l’usage CPU. Au retour, resume est émis avant pageshow pour les pages restaurées.

La conséquence architecturale est claire : le code doit pouvoir s’arrêter, puis reprendre proprement. Les ressources externes doivent être relâchées, et l’interface doit pouvoir être reconstituée sans dépendre d’une exécution continue du moteur JavaScript.

Séparer l’état métier de l’état de rendu

Le pattern le plus fiable consiste à séparer l’« état persistant » et l’« état de rendu ». L’état métier important, données de formulaire, brouillons, progression, préférences, sélection courante, doit être écrit dans un stockage local ou côté serveur. Les détails purement visuels peuvent, eux, être reconstruits au retour.

Cette distinction évite de confondre ce qui doit survivre à une suppression d’onglet et ce qui n’est qu’un confort d’interface. Par exemple, l’onglet actif, le scroll, le focus ou une animation ne méritent pas le même niveau de persistance qu’une saisie utilisateur non soumise ou un panier d’achat.

Dans les applications complexes, cette séparation améliore aussi la testabilité. On peut valider indépendamment la logique de persistance, la restauration d’interface et la reprise des connexions, ce qui réduit les effets de bord lors des transitions de visibilité, de gel ou de restauration depuis le cache.

Persister les données sans attendre : stratégies pratiques

Pour éviter de perdre l’état de saisie, il faut sauvegarder souvent et de façon incrémentale. Chrome souligne que les formulaires contenant une entrée utilisateur non soumise rendent le gel ou la suppression plus risqués. Attendre le dernier moment expose donc à une perte de données difficile à récupérer.

Dans de nombreux cas, visibilitychange suffit comme déclencheur principal. Dès que la page passe en arrière-plan, on enregistre les modifications locales, on pousse éventuellement un état partiel vers le serveur, puis on coupe les tâches inutiles. Cela réduit le travail en arrière-plan tout en augmentant la résilience.

Si vous utilisez IndexedDB, le commit sur IDBTransaction est particulièrement utile pour forcer l’écriture sans dépendre d’autres tâches asynchrones. C’est une bonne pratique dans un gestionnaire de freeze ou de visibilitychange, car elle permet de sécuriser les données avant qu’un gel n’interrompe le reste du traitement.

Réduire les erreurs au retour de l’onglet

Le retour d’un onglet ne doit pas être traité comme un simple rafraîchissement d’UI. Il peut s’agir d’un resume, d’un pageshow, ou d’une restauration depuis le bfcache. Le code doit donc savoir réinitialiser les listeners, rouvrir les connexions et recharger les données si nécessaire.

Les pages qui jouent de l’audio, utilisent WebRTC, mettent à jour le titre ou la favicon, affichent des alertes ou envoient des notifications push sont traitées plus conservativement par Chrome. Ce comportement peut retarder ou modifier la manière dont l’onglet est gelé, mais il ne faut pas y voir une garantie de continuité.

En pratique, le bon réflexe est de valider l’état de l’application au retour : la session est-elle encore valide, les données locales sont-elles cohérentes, faut-il re-synchroniser avec le serveur ? Cette vérification rend l’interface plus robuste face aux divergences entre navigateurs et plateformes.

Éviter les pièges classiques : timers, beforeunload et suppositions fragiles

Deux erreurs reviennent souvent : compter sur les timers d’arrière-plan et compter sur beforeunload ou unload pour sauvegarder. Or les navigateurs limitent fortement les callbacks cachés, et beforeunload n’est pas fiable dans plusieurs scénarios, notamment sur mobile. Le signal hidden est généralement bien plus prévisible.

De même, un onglet caché ne doit jamais être considéré comme un environnement de calcul continu. Les timers peuvent être ralentis, suspendus ou alignés selon les politiques du navigateur. Les traitements critiques doivent donc être déclenchés par des événements explicites, pas par l’espoir qu’un intervalle se réveillera à temps.

Le message de conception est simple : traitez un onglet caché comme potentiellement en fin de vie. Cette approche, explicitement recommandée par Chrome, permet de construire des applications plus résilientes face au gel, à la suppression et au redémarrage du rendu.

Concevoir pour un web non fiable en arrière-plan

Le comportement varie selon le navigateur, mais la direction est commune : un onglet en arrière-plan n’est pas un lieu sûr pour exécuter du travail important. MDN comme Chrome convergent sur le même constat : il faut coder pour un environnement non fiable dès que la page n’est plus visible.

Pour les produits web à forte valeur métier, cette contrainte devient une opportunité de robustesse. En adoptant les bons hooks de cycle de vie, une persistance incrémentale et une séparation nette entre état métier et état d’interface, on protège la donnée utilisateur et on améliore l’expérience globale.

Chez Hurter & Co, nous recommandons de traiter la visibilité comme un signal d’architecture, pas seulement comme un détail d’ergonomie. C’est souvent ce changement de perspective qui transforme une web app “qui marche en démo” en un produit fiable dans les conditions réelles d’usage.

En pratique, le bon système est celui qui sait s’arrêter sans perdre d’informations, puis repartir sans surprise. Dans un web de plus en plus contraint par l’économie de ressources, cette capacité n’est plus un bonus technique : c’est une exigence produit.