Comparaison des stockages du navigateur : localStorage, IndexedDB, Cache API et OPFS

技术架构

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.

Essayez ces outils exécutés localement dans le navigateur — aucune inscription requise →

#localStorage#sessionStorage#IndexedDB#Cache API#存储对比