Хранилища: IndexedDB и localStorage

Урок сравнивает хранилища браузера и объясняет, когда применять localStorage, IndexedDB и Cache API.

IndexedDB — встроенная в браузер база данных для структурированных объектов; в PWA её используют для офлайн-данных приложения.

Зачем PWA несколько хранилищ

Офлайн-приложению нужно где-то держать данные: настройки, черновики, кеш ответов API, очередь отправки. Под разные задачи — разные хранилища, у каждого свои сильные и слабые стороны.

ХранилищеДля чегоОсобенности
localStorageмелкие настройки, флаги, токенысинхронный, только строки, ~5 МБ
IndexedDBструктурированные данные, офлайн-записи, очередиасинхронный, объекты, большой объём
Cache APIответы на сетевые запросы (файлы, API)асинхронный, пары запрос → ответ

localStorage — просто, но ограниченно

Самое простое хранилище: ключ-значение, только строки, синхронный API.

localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
console.log('Тема:', theme);

// объекты приходится сериализовать вручную
localStorage.setItem('user', JSON.stringify({ name: 'Аня', age: 25 }));
const user = JSON.parse(localStorage.getItem('user'));
console.log('Имя:', user.name);

Вывод:

Тема: dark
Имя: Аня

Этот пример помечен language-javascript и исполняется: localStorage работает и в обычной странице. Но из Service Worker он недоступен (там нет window), и он синхронный — большие данные через него хранить нельзя.

IndexedDB — для серьёзных данных

IndexedDB хранит объекты, поддерживает индексы и транзакции, вмещает много данных и работает асинхронно. Доступен и со страницы, и из Service Worker. Минус — «сыроватый» низкоуровневый API, поэтому на практике берут обёртку (например, библиотеку idb).

const req = indexedDB.open('app-db', 1);
req.onupgradeneeded = function (e) {
  const db = e.target.result;
  db.createObjectStore('drafts', { keyPath: 'id' });
};
req.onsuccess = function (e) {
  const db = e.target.result;
  const tx = db.transaction('drafts', 'readwrite');
  tx.objectStore('drafts').put({ id: 1, text: 'Черновик' });
};

Помечено language-text: пример опирается на событийный API IndexedDB, который удобнее показывать как иллюстрацию.

Как работает под капотом выбор хранилища

Правило простое: что храним и где к этому обращаемся. Мелкая настройка, нужная только на странице, — localStorage. Структурированные офлайн-данные или очередь для Background Sync, к которой обращается и страница, и Service Worker, — IndexedDB. Сетевые ответы для офлайн-выдачи — Cache API. Эти хранилища не конкурируют, а дополняют друг друга: типичное PWA использует все три.

Частые ошибки

  • Хранить большие данные в localStorage. Лимит ~5 МБ, синхронность тормозит интерфейс.
  • Обращаться к localStorage из Service Worker. Там его нет — используйте IndexedDB.
  • Забывать про сериализацию. localStorage хранит только строки; объекты — через JSON.stringify/parse.
  • Писать IndexedDB «руками» в больших проектах. Низкоуровневый API многословен; берите обёртку.

Итоги

  • localStorage — мелочи, строки, синхронно, только со страницы.
  • IndexedDB — структурированные данные и очереди, асинхронно, доступен и в Service Worker.
  • Cache API — ответы на запросы для офлайна.
  • Типичное PWA использует все три по назначению.
Проверьте себя
1. Какое хранилище доступно из Service Worker для структурированных данных?
AlocalStorage
BIndexedDB
CsessionStorage
Ddocument.cookie напрямую
2. Почему localStorage не подходит для больших объёмов данных?
AОн работает только офлайн
BОн синхронный, хранит только строки и ограничен примерно 5 МБ
CОн требует HTTPS
DОн недоступен в Chrome