Présentation des cinq mécanismes de stockage du navigateur
Les navigateurs modernes offrent 5 mécanismes de stockage principaux, chacun conçu pour des objectifs et des cas d'usage différents :
| Stockage |
Type de données |
Limite de capacité |
Persistance |
Accès par thread |
| localStorage |
Clé-valeur chaîne |
~5-10 Mo |
Permanent |
Thread principal uniquement |
| sessionStorage |
Clé-valeur chaîne |
~5-10 Mo |
Fermeture d'onglet |
Thread principal uniquement |
| IndexedDB |
Données structurées + Blob |
Centaines de Mo~illimité |
Permanent |
Thread principal + Worker |
| Cache API |
Paires Request/Response |
Partagé avec le quota IndexedDB |
Permanent |
Thread principal + Worker |
| OPFS |
Système de fichiers |
Partagé avec le quota IndexedDB |
Permanent |
Worker uniquement (synchrone) |
localStorage et sessionStorage
Utilisation de base
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
console.log(key, localStorage.getItem(key));
}
Différences clés
| Caractéristique |
localStorage |
sessionStorage |
| Durée de vie |
Permanent |
Effacé à la fermeture de l'onglet |
| Portée |
Partagé entre les onglets de même origine |
Onglet courant uniquement |
| Usage typique |
Préférences utilisateur, tokens |
Brouillons de formulaire, état de page |
Détection de capacité
function testLocalStorageQuota() {
const testKey = '__quota_test__';
let data = '';
try {
for (let i = 0; i < 1024; i++) data += 'a'.repeat(1024);
for (let mb = 1; mb < 20; mb++) {
localStorage.setItem(testKey, data.repeat(mb));
}
} catch (e) {
console.log('localStorage quota exceeded');
} finally {
localStorage.removeItem(testKey);
}
}
Mises en garde
- Blocage synchrone : La lecture/écriture bloque le thread principal ; les données volumineuses dégradent les performances
- Chaînes uniquement : Les objets nécessitent
JSON.stringify/JSON.parse
- Événement storage : Déclenche l'événement
storage dans les autres onglets de même origine lors d'une modification
IndexedDB
Utilisation de base
const openRequest = indexedDB.open('myDatabase', 1);
openRequest.onupgradeneeded = (event) => {
const db = event.target.result;
const store = db.createObjectStore('users', { keyPath: 'id' });
store.createIndex('name', 'name', { unique: false });
};
openRequest.onsuccess = (event) => {
const db = event.target.result;
const tx = db.transaction('users', 'readwrite');
const store = tx.objectStore('users');
store.add({ id: 1, name: 'Alice', email: 'alice@example.com' });
store.get(1).onsuccess = (e) => {
console.log(e.target.result);
};
};
Fonctionnalités avancées
| Fonctionnalité |
Description |
| Transactions |
Garantit l'atomicité, séparation lecture/écriture |
| Index |
Requêtes efficaces sur les champs non-clés |
| Curseurs |
Itérer de grands jeux de données sans tout charger en mémoire |
| Stockage Blob |
Stocker directement les objets File et Blob |
| Accès Worker |
Disponible dans les Service Workers et Web Workers |
Simplifié avec la bibliothèque idb
import { openDB } from 'idb';
const db = await openDB('myDatabase', 1, {
upgrade(db) {
db.createObjectStore('users', { keyPath: 'id' });
}
});
await db.put('users', { id: 1, name: 'Alice' });
const user = await db.get('users', 1);
const allUsers = await db.getAll('users');
await db.delete('users', 1);
Cache API
Utilisation de base
const cache = await caches.open('v1');
await cache.put('/api/data', new Response(JSON.stringify({ foo: 'bar' })));
const response = await cache.match('/api/data');
const data = await response.json();
await cache.delete('/api/data');
Avec Service Worker
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached;
return fetch(event.request).then((response) => {
const clone = response.clone();
caches.open('v1').then((cache) => {
cache.put(event.request, clone);
});
return response;
});
})
);
});
Comparaison des stratégies de cache
| Stratégie |
Description |
Cas d'usage |
| Cache First |
Cache en premier, réseau en cas d'absence |
Ressources statiques, polices |
| Network First |
Réseau en premier, cache en secours |
Données d'API, contenu fréquemment mis à jour |
| Stale While Revalidate |
Retourner le cache, mettre à jour en arrière-plan |
APIs non critiques, images |
| Cache Only |
Cache uniquement |
Pages hors ligne, ressources pré-mises en cache |
| Network Only |
Réseau uniquement |
Requêtes non-GET, données en temps réel |
OPFS (Origin Private File System)
Utilisation de base
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1, 2, 3, 4]));
await writable.close();
const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();
Accès synchrone dans les Workers
// Dans un Worker — OPFS synchrone (extrêmement rapide)
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('large.bin', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();
const buffer = new ArrayBuffer(4);
accessHandle.write(buffer, { at: 0 });
accessHandle.flush();
accessHandle.close();
OPFS vs File System Access API
| Caractéristique |
OPFS |
File System Access API |
| Autorisation utilisateur |
Non requise |
L'utilisateur doit choisir le répertoire |
| Visibilité |
Système de fichiers virtuel interne au navigateur |
Vrai système de fichiers du système d'exploitation |
| Sync dans Worker |
Pris en charge |
Non pris en charge |
| Persistance |
Permanent |
Dépend de l'autorisation utilisateur |
| Cas d'usage |
Traitement de fichiers temporaires haute performance |
Éditeurs de fichiers, export |
Quota de stockage et persistance
Interroger le quota disponible
const estimate = await navigator.storage.estimate();
console.log(`Used: ${(estimate.usage / 1024 / 1024).toFixed(2)} MB`);
console.log(`Quota: ${(estimate.quota / 1024 / 1024).toFixed(2)} MB`);
Demander la persistance
const persisted = await navigator.storage.persist();
if (persisted) {
console.log('Storage persisted, will not be auto-evicted');
} else {
console.log('Storage may be cleared under pressure');
}
Les navigateurs peuvent automatiquement supprimer le stockage non persistant sous pression. Appelez navigator.storage.persist() pour demander la persistance.
Guide de décision
What do you need to store?
├── Simple key-value (< 5MB)
│ ├── Cross-tab sharing → localStorage
│ └── Current tab only → sessionStorage
├── Structured data / many records
│ └── IndexedDB
├── HTTP request/response caching
│ └── Cache API
└── Large files / binary / high-performance I/O
└── OPFS
Exemples concrets
| Scénario |
Stockage recommandé |
Raison |
| Préférence de thème utilisateur |
localStorage |
Clé-valeur simple, inter-pages |
| Brouillon de formulaire |
sessionStorage |
Temporaire, limité à l'onglet |
| Données d'outil hors ligne |
IndexedDB |
Structuré, requêtes indexées |
| Cache de ressources statiques |
Cache API |
Conçu pour Request/Response |
| Fichiers temporaires de traitement vidéo |
OPFS |
E/S synchrone en Worker, haute performance |
Considérations de sécurité
- Risque XSS : localStorage est lisible par XSS — ne jamais stocker de tokens sensibles
- Cookies HttpOnly : Les tokens d'authentification doivent utiliser des cookies HttpOnly + Secure
- Chiffrement du stockage : Chiffrez les données sensibles avec JWT ou Hash avant de les stocker
- Isolation de même origine : Tout le stockage suit la politique de même origine ; l'accès cross-origin est bloqué
Résumé
Le stockage du navigateur a évolué d'un simple localStorage vers un système complet couvrant les paires clé-valeur, les données structurées, le cache HTTP et les systèmes de fichiers. La clé de la sélection est la correspondance avec les caractéristiques des données : configuration simple → localStorage, données structurées → IndexedDB, cache HTTP → Cache API, E/S fichiers haute performance → OPFS.
Utilisez le Parseur de Cookies pour inspecter la configuration des cookies, l'Outil JWT pour vérifier la sécurité des tokens, et l'Outil Hash pour hacher les données sensibles.