Il existe de nombreuses options pour stocker des données dans le navigateur. Lequel est le mieux adapté à vos besoins ?
Les connexions Internet peuvent être instables ou inexistantes lors de vos déplacements. C'est pourquoi la compatibilité hors connexion et des performances fiables sont des fonctionnalités courantes des progressive web apps. Même dans des environnements sans fil parfaits, l'utilisation judicieuse de la mise en cache et d'autres techniques de stockage peut améliorer considérablement l'expérience utilisateur. Il existe plusieurs façons de mettre en cache les ressources d'application statiques (HTML, JavaScript, CSS, images, etc.) et les données (données utilisateur, articles d'actualités, etc.). Mais quelle est la meilleure solution ? Quelle quantité de données pouvez-vous stocker ? Comment l'empêcher d'être expulsé ?
Que dois-je utiliser ?
Voici une recommandation générale pour le stockage des ressources :
- Pour les ressources réseau nécessaires au chargement de votre application, utilisez l'API Cache Storage (qui fait partie des service workers).
- Pour le contenu basé sur des fichiers, utilisez le système de fichiers privé d'origine (OPFS).
- Pour les autres données, utilisez IndexedDB (avec un wrapper de promesses).
IndexedDB, l'OPFS et l'API Cache Storage sont compatibles avec tous les navigateurs récents.
Elles sont asynchrones et ne bloquent pas le thread principal (mais il existe également une variante synchrone de l'OPFS qui n'est disponible que dans les Web Workers). Elles sont accessibles depuis l'objet window, les Web Workers et les Service Workers, ce qui permet de les utiliser n'importe où dans votre code.
Qu'en est-il des autres mécanismes de stockage ?
Il existe plusieurs autres mécanismes de stockage disponibles dans le navigateur, mais leur utilisation est limitée et ils peuvent entraîner d'importants problèmes de performances.
SessionStorage est spécifique à l'onglet et limité à la durée de vie de l'onglet. Il peut être utile pour stocker de petites quantités d'informations spécifiques à la session, par exemple une clé IndexedDB. Elle doit être utilisée avec précaution, car elle est synchrone et bloque le thread principal. Il est limité à environ 5 Mo et ne peut contenir que des chaînes. Comme il est spécifique à un onglet, il n'est pas accessible depuis les Web Workers ou les Service Workers.
Évitez d'utiliser LocalStorage, car il est synchrone et bloque le thread principal. Il est limité à environ 5 Mo et ne peut contenir que des chaînes. LocalStorage n'est pas accessible depuis les web workers ou les service workers.
Les cookies ont leur utilité, mais ne doivent pas être utilisés pour le stockage. Les cookies sont envoyés avec chaque requête HTTP. Par conséquent, le stockage d'une quantité de données supérieure à une petite quantité augmente considérablement la taille de chaque requête Web. Elles sont synchrones et ne sont pas accessibles depuis les Web Workers. Comme LocalStorage et SessionStorage, les cookies sont limités aux chaînes.
L'API File System Access a été conçue pour permettre aux utilisateurs de lire et de modifier des fichiers sur leur système de fichiers local. L'utilisateur doit accorder l'autorisation avant qu'une page puisse lire ou écrire dans un fichier local. Les autorisations ne sont pas conservées d'une session à l'autre, sauf si un handle de fichier est mis en cache dans IndexedDB. L'API File System Access est particulièrement adaptée aux cas d'utilisation tels que les éditeurs, où vous devez ouvrir un fichier, le modifier, puis éventuellement enregistrer les modifications dans le fichier.
L'API File System et l'API FileWriter fournissent des méthodes pour lire et écrire des fichiers dans un système de fichiers en bac à sable. Bien qu'elle soit asynchrone, elle n'est pas recommandée, car elle n'est disponible que dans les navigateurs basés sur Chromium.
Quelle quantité de données puis-je stocker ?
En bref, beaucoup, au moins quelques centaines de mégaoctets, et potentiellement des centaines de gigaoctets ou plus. Les implémentations des navigateurs varient, mais la quantité d'espace de stockage disponible dépend généralement de la quantité d'espace de stockage disponible sur l'appareil.
- Chrome permet au navigateur d'utiliser jusqu'à 80 % de l'espace disque total. Une origine peut utiliser jusqu'à 60 % de l'espace disque total. Vous pouvez utiliser l'API StorageManager pour déterminer le quota maximal disponible. Les autres navigateurs basés sur Chromium peuvent être différents.
- En mode navigation privée, Chrome réduit la quantité d'espace de stockage qu'une origine peut utiliser à environ 5 % de l'espace disque total.
- Si l'utilisateur a activé l'option "Effacer les cookies et les données des sites à la fermeture du navigateur" dans Chrome, le quota de stockage est considérablement réduit à un maximum d'environ 300 Mo.
- Firefox permet au navigateur d'utiliser jusqu'à 50 % de l'espace disque disponible. Un groupe eTLD+1 (par exemple,
example.com,www.example.cometfoo.bar.example.com) peut utiliser jusqu'à 2 Go. Vous pouvez utiliser l'API StorageManager pour déterminer l'espace encore disponible. - Safari (sur ordinateur et mobile) semble autoriser environ 1 Go. Lorsque la limite est atteinte, Safari invite l'utilisateur à l'augmenter par incréments de 200 Mo. Je n'ai pas trouvé de documentation officielle à ce sujet.
- Si une PWA est ajoutée à l'écran d'accueil sur Safari mobile, elle crée un conteneur de stockage, et rien n'est partagé entre la PWA et Safari mobile. Une fois le quota atteint pour une PWA installée, il ne semble pas possible de demander de l'espace de stockage supplémentaire.
Auparavant, si un site dépassait un certain seuil de données stockées, le navigateur invitait l'utilisateur à autoriser l'utilisation de données supplémentaires. Par exemple, si l'origine utilisait plus de 50 Mo, le navigateur invitait l'utilisateur à l'autoriser à stocker jusqu'à 100 Mo, puis lui demandait à nouveau par incréments de 50 Mo.
Aujourd'hui, la plupart des navigateurs récents n'invitent pas l'utilisateur à agir et permettent à un site d'utiliser l'intégralité de son quota. L'exception semble être Safari, qui affiche une invite lorsque le quota de stockage est dépassé, demandant l'autorisation d'augmenter le quota alloué. Si une origine tente d'utiliser plus que son quota, les tentatives d'écriture de données supplémentaires échoueront.
Comment vérifier l'espace de stockage disponible ?
Dans de nombreux navigateurs, vous pouvez utiliser l'API StorageManager pour déterminer la quantité d'espace de stockage disponible pour l'origine et la quantité d'espace de stockage qu'elle utilise. Elle indique le nombre total d'octets utilisés par IndexedDB et l'API Cache, et permet de calculer l'espace de stockage restant approximatif disponible.
if (navigator.storage && navigator.storage.estimate) {
const quota = await navigator.storage.estimate();
// quota.usage -> Number of bytes used.
// quota.quota -> Maximum number of bytes available.
const percentageUsed = (quota.usage / quota.quota) * 100;
console.log(`You've used ${percentageUsed}% of the available storage.`);
const remaining = quota.quota - quota.usage;
console.log(`You can write up to ${remaining} more bytes.`);
}
Vous devez intercepter les erreurs de dépassement de quota (voir ci-dessous). Dans certains cas, il est possible que le quota disponible dépasse la quantité réelle d'espace de stockage disponible.
Inspecter
Pendant le développement, vous pouvez utiliser les outils de développement de votre navigateur pour inspecter les différents types de stockage et effacer toutes les données stockées.
Une nouvelle fonctionnalité a été ajoutée dans Chrome 88. Elle vous permet de remplacer le quota de stockage du site dans le volet "Stockage". Cette fonctionnalité vous permet de simuler différents appareils et de tester le comportement de vos applications dans des scénarios de faible disponibilité du disque. Accédez à Application, puis à Storage (Stockage). Cochez la case Simulate custom storage quota (Simuler un quota de stockage personnalisé), puis saisissez un nombre valide pour simuler le quota de stockage.
Comment gérer le dépassement de quota ?
Que devez-vous faire lorsque vous dépassez le quota ? Plus important encore, vous devez toujours intercepter et gérer les erreurs d'écriture, qu'il s'agisse d'une erreur QuotaExceededError ou autre. Ensuite, en fonction de la conception de votre application, décidez de la manière de la gérer.
Par exemple, supprimez le contenu auquel l'utilisateur n'a pas accédé depuis longtemps, supprimez les données en fonction de leur taille ou permettez aux utilisateurs de choisir ce qu'ils souhaitent supprimer.
IndexedDB et l'API Cache génèrent une erreur DOMError nommée QuotaExceededError lorsque vous dépassez le quota disponible.
IndexedDB
Si l'origine a dépassé son quota, les tentatives d'écriture dans IndexedDB échoueront. Le gestionnaire onabort() de la transaction sera appelé, en transmettant un événement.
L'événement inclura un DOMException dans la propriété d'erreur. La vérification de l'erreur name renvoie QuotaExceededError.
const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
const error = event.target.error; // DOMException
if (error.name == 'QuotaExceededError') {
// Fallback code goes here
}
};
API Cache
Si l'origine a dépassé son quota, les tentatives d'écriture dans l'API Cache seront rejetées avec une erreur QuotaExceededError DOMException.
try {
const cache = await caches.open('my-cache');
await cache.add(new Request('/sample1.jpg'));
} catch (err) {
if (error.name === 'QuotaExceededError') {
// Fallback code goes here
}
}
Comment fonctionne l'éviction ?
Le stockage Web est classé en deux catégories : "Best Effort" et "Persistent". Le stockage "best effort" signifie que le stockage peut être effacé par le navigateur sans interrompre l'utilisateur, mais qu'il est moins durable pour les données critiques ou à long terme. Le stockage persistant n'est pas automatiquement effacé lorsque l'espace de stockage est faible. L'utilisateur doit effacer manuellement cet espace de stockage (via les paramètres du navigateur).
Par défaut, les données d'un site (y compris IndexedDB, l'API Cache, etc.) appartiennent à la catégorie "best effort". Cela signifie que, sauf si un site a demandé un stockage persistant, le navigateur peut supprimer les données du site à sa discrétion, par exemple lorsque l'espace de stockage de l'appareil est faible.
La règle d'éviction pour le niveau de service "best effort" est la suivante :
- Les navigateurs basés sur Chromium commencent à supprimer des données lorsque le navigateur manque d'espace, en effaçant toutes les données de site de l'origine la moins récemment utilisée, puis de la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
- Firefox commence à supprimer des données lorsque l'espace disque disponible est plein, en effaçant toutes les données de site de l'origine la moins récemment utilisée, puis de la suivante, jusqu'à ce que le navigateur ne dépasse plus la limite.
- Auparavant, Safari n'expulsait pas les données, mais a récemment mis en place une nouvelle limite de sept jours pour tous les espaces de stockage accessibles en écriture (voir ci-dessous).
À partir d'iOS et iPadOS 13.4 et de Safari 13.1 sur macOS, une limite de sept jours est appliquée à tous les espaces de stockage accessibles en écriture par script, y compris IndexedDB, l'enregistrement des service workers et l'API Cache. Cela signifie que Safari supprimera tout le contenu du cache après sept jours d'utilisation de Safari si l'utilisateur n'interagit pas avec le site. Cette règle d'éviction ne s'applique pas aux PWA installées qui ont été ajoutées à l'écran d'accueil. Pour en savoir plus, consultez l'article Full Third-Party Cookie Blocking and More (Blocage complet des cookies tiers et plus encore) sur le blog WebKit.
Buckets de stockage
L'idée principale de l'API Storage Buckets est d'accorder aux sites la possibilité de créer plusieurs buckets de stockage, où le navigateur peut choisir de supprimer chaque bucket indépendamment des autres. Cela permet aux développeurs de spécifier la hiérarchisation de l'éviction pour s'assurer que les données les plus importantes ne sont pas supprimées.
Bonus : Pourquoi utiliser un wrapper pour IndexedDB ?
IndexedDB est une API de bas niveau qui nécessite une configuration importante avant d'être utilisée, ce qui peut être particulièrement fastidieux pour stocker des données peu complexes. Contrairement à la plupart des API modernes basées sur des promesses, elle est basée sur des événements. Les wrappers de promesses comme idb pour IndexedDB masquent certaines des fonctionnalités puissantes, mais surtout, ils masquent les mécanismes complexes (par exemple, les transactions, la gestion des versions de schéma) qui accompagnent la bibliothèque IndexedDB.
Bonus : SQLite Wasm
Après l'abandon et la suppression de Web SQL de Chrome, Google a collaboré avec les responsables de la base de données SQLite populaire pour proposer un remplacement de Web SQL basé sur SQLite. Pour savoir comment l'utiliser, consultez SQLite Wasm dans le navigateur, soutenu par le système de fichiers privé d'origine.
Conclusion
L'époque où l'espace de stockage était limité et où l'utilisateur était invité à stocker de plus en plus de données est révolue. Les sites peuvent stocker efficacement toutes les ressources et données dont ils ont besoin pour s'exécuter. L'API StorageManager vous permet de déterminer l'espace disponible et l'espace utilisé. De plus, avec le stockage persistant, vous pouvez protéger les données contre l'éviction, sauf si l'utilisateur les supprime.
Ressources supplémentaires
Merci
Nous remercions tout particulièrement Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink et Victor Costan d'avoir examiné ce guide. Merci à Eiji Kitamura, Addy Osmani et Marc Cohen, qui ont rédigé les articles d'origine sur lesquels ce document est basé. Eiji a écrit un outil utile appelé Browser Storage Abuser, qui a permis de valider le comportement actuel. Il vous permet de stocker autant de données que possible et de voir les limites de stockage de votre navigateur. Merci à François Beaufort, qui a étudié Safari pour déterminer ses limites de stockage, et à Thomas Steiner, qui a ajouté des informations sur le système de fichiers privé d'origine, les buckets de stockage et SQLite Wasm, et qui a mis à jour l'ensemble du contenu en 2024.