Сравнение хранилищ браузера: localStorage, IndexedDB, Cache API и OPFS

技术架构

Обзор пяти механизмов хранения браузера

Современные браузеры предлагают 5 основных механизмов хранения, каждый из которых предназначен для разных целей и сценариев использования:

Хранилище Тип данных Лимит ёмкости Персистентность Доступ из потоков
localStorage Строковые ключ-значение ~5-10 МБ Постоянное Только главный поток
sessionStorage Строковые ключ-значение ~5-10 МБ При закрытии вкладки Только главный поток
IndexedDB Структурированные данные + Blob Сотни МБ~без ограничений Постоянное Главный поток + Worker
Cache API Пары Request/Response Общая квота с IndexedDB Постоянное Главный поток + Worker
OPFS Файловая система Общая квота с IndexedDB Постоянное Только Worker (синхронно)

localStorage и sessionStorage

Базовое использование

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));
}

Ключевые различия

Свойство localStorage sessionStorage
Время жизни Постоянное Очищается при закрытии вкладки
Область видимости Общее для вкладок одного источника Только текущая вкладка
Типичное использование Настройки пользователя, токены Черновики форм, состояние страницы

Определение ёмкости

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);
  }
}

Предостережения

  • Синхронная блокировка: Чтение/запись блокирует главный поток; большие данные ухудшают производительность
  • Только строки: Объектам требуется JSON.stringify/JSON.parse
  • Событие storage: Вызывает событие storage в других вкладках того же источника при изменении

IndexedDB

Базовое использование

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);
  };
};

Расширенные возможности

Возможность Описание
Транзакции Гарантируют атомарность, разделение чтения/записи
Индексы Эффективные запросы по неключевым полям
Курсоры Итерация больших наборов данных без загрузки всех сразу
Хранение Blob Прямое хранение объектов File и Blob
Доступ из Worker Доступно в Service Workers и Web Workers

Упрощение с библиотекой 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

Базовое использование

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');

С 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;
      });
    })
  );
});

Сравнение стратегий кэширования

Стратегия Описание Сценарий использования
Cache First Сначала кэш, сеть при промахе Статические ресурсы, шрифты
Network First Сначала сеть, кэш как резерв Данные API, часто обновляемый контент
Stale While Revalidate Вернуть кэш, обновить в фоновом режиме Некритичные API, изображения
Cache Only Только кэш Офлайн-страницы, предварительно кэшированные ресурсы
Network Only Только сеть Non-GET запросы, данные в реальном времени

OPFS (Origin Private File System)

Базовое использование

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();

Синхронный доступ в Worker

// В Worker — синхронный OPFS (крайне быстрый)
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

Свойство OPFS File System Access API
Разрешение пользователя Не требуется Пользователь должен выбрать каталог
Видимость Внутренняя виртуальная ФС браузера Реальная ФС операционной системы
Синхронность в Worker Поддерживается Не поддерживается
Персистентность Постоянное Зависит от разрешения пользователя
Сценарий использования Высокопроизводительная обработка временных файлов Файловые редакторы, экспорт

Квота хранилища и персистентность

Запрос доступной квоты

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`);

Запрос персистентности

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');
}

Браузеры могут автоматически удалять неперсистентное хранилище при нехватке ресурсов. Вызовите navigator.storage.persist(), чтобы запросить персистентность.


Руководство по выбору

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

Практические примеры

Сценарий Рекомендуемое хранилище Причина
Настройка темы пользователя localStorage Простой ключ-значение, между страницами
Черновик формы sessionStorage Временное, в пределах вкладки
Данные офлайн-инструмента IndexedDB Структурированные, индексированные запросы
Кэш статических ресурсов Cache API Разработано для Request/Response
Временные файлы обработки видео OPFS Синхронный ввод-вывод в Worker, высокая производительность

Вопросы безопасности

  • Риск XSS: localStorage доступен для чтения через XSS — никогда не храните конфиденциальные токены
  • HttpOnly-куки: Токены аутентификации должны использовать HttpOnly + Secure куки
  • Шифрование хранилища: Шифруйте конфиденциальные данные с помощью JWT или Hash перед сохранением
  • Изоляция одного источника: Все хранилища следуют политике одного источника; межисточниковый доступ заблокирован

Заключение

Хранилище браузера эволюционировало от единственного localStorage до полноценной системы, охватывающей пары ключ-значение, структурированные данные, HTTP-кэширование и файловые системы. Ключ к выбору — соответствие характеристикам данных: простая конфигурация → localStorage, структурированные данные → IndexedDB, HTTP-кэширование → Cache API, высокопроизводительный файловый ввод-вывод → OPFS.

Используйте Парсер куки для проверки конфигурации куки, Инструмент JWT для проверки безопасности токенов и Инструмент Hash для хеширования конфиденциальных данных.

Попробуйте эти локальные браузерные инструменты — регистрация не требуется →

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