Хранилища: 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 использует все три по назначению.