IndexedDB хранение данных в браузере открывает возможности, которые далеко выходят за рамки простого localStorage. Это не просто место для пар ключ-значение, а полноценная клиентская база данных, способная сохранять сложные объекты, большие файлы и работать в офлайн режиме.
В этой статье я расскажу, как работает IndexedDB, какие архитектурные решения стоит принять и какие ошибки чаще всего встречаются при реализации. Материал ориентирован на практические задачи — от простого CRUD до синхронизации и отладки.
Почему IndexedDB — это не просто ещё один способ хранить данные
В отличие от cookie или localStorage, IndexedDB асинхронна и заточена под хранение больших объёмов данных. Она позволяет сохранять объекты в исходной структуре благодаря алгоритму structured clone, что особенно удобно для приложений с множеством сложных состояний.
IndexedDB подходит для офлайн-приложений, кэширования ответов API, хранения медиафайлов и работы с большими наборами записей. Если вам нужен быстрый доступ к данным без постоянного обращения к серверу, это один из лучших инструментов в браузере.
Ключевые понятия и устройство базы
База IndexedDB состоит из баз данных, в каждой из которых есть object store. Object store — аналог таблицы, но без строгой схемы. Для быстрого поиска используются индексы, которые вы создаёте вручную при изменении версии базы.
Операции в IndexedDB оборачиваются запросами и транзакциями. Транзакция задаёт набор object store, режим доступа и гарантирует атомарность операций внутри неё. Версия базы определяет миграции и структуру, а событие onupgradeneeded служит для создания или изменения хранилищ и индексов.
Основные операции: что и как делать
Частые операции — get, put, add, delete, clear. put вставляет или обновляет запись, add требует уникальности ключа. Для перебора записей используют openCursor, а для массового получения существуют getAll и getAllKeys.
Транзакции бывают readonly и readwrite. Они живут до тех пор, пока не завершатся все запросы внутри. Ошибка в одном запросе откатывает транзакцию, что удобно для сохранения целостности данных.
Пошаговое использование: от открытия к записи
Сначала открывают подключение к базе методом indexedDB.open с указанием имени и, при необходимости, версии. В обработчике onupgradeneeded создают object store и индексы. После успешного открытия можно начинать транзакции и выполнять запросы.
Ниже небольшой пример базового шаблона для добавления записи. Код даёт представление о последовательности событий и о том, где стоит размещать логику создания схемы.
const request = indexedDB.open('notes-db', 1);
request.onupgradeneeded = event => {
const db = event.target.result;
if (!db.objectStoreNames.contains('notes')) {
const store = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
store.createIndex('by-date', 'updatedAt');
}
};
request.onsuccess = event => {
const db = event.target.result;
const tx = db.transaction('notes', 'readwrite');
const store = tx.objectStore('notes');
store.put({ title: 'Дело', content: 'Сделать уборку', updatedAt: Date.now() });
tx.oncomplete = () => db.close();
};
Реальный кейс: офлайн блокнот и синхронизация
Когда я делал офлайн блокнот для личных задач, IndexedDB стала спасением. Пользователь мог создавать заметки без подключения, а при появлении сети приложение шло по списку изменений и отправляло их на сервер.
Главная проблема, с которой столкнулся — управление версиями. При добавлении новых полей потребовалось аккуратно мигрировать старые записи, не потеряв данные. Решение простое: в onupgradeneeded писать понятные миграции и тестировать их на реальных бэкапах.
Стратегии синхронизации и кеширования
Для синхронизации полезно хранить метаданные: локальную временную метку, статус синхронизации и хеш содержимого. Это позволяет минимизировать пересылку данных и корректно разрешать конфликты при параллельных правках.
Если приложение использует service worker, IndexedDB часто применяется в связке с Cache API: один отвечает за ответы сервера, другой за структуру данных и черновики. Такой подход даёт гибкость и экономит сетевой трафик.
Сравнение с другими хранилищами
Ниже таблица с упрощённым сравнением IndexedDB, localStorage и Cache API по основным характеристикам. Это поможет выбрать инструмент в зависимости от задачи.
| Характеристика | localStorage | IndexedDB | Cache API |
|---|---|---|---|
| Объём | Малый, синхронный | Большие объёмы, асинхронно | Оптимизирован для ответов сети |
| Тип данных | Строки | Сложные объекты, Blobs | Ответы Request/Response |
| Использование | Небольшие настройки | Базы приложений, офлайн | Кеширование ресурсов |
Ограничения, безопасность и особенности
IndexedDB привязана к origin, поэтому у каждой страницы свои данные. В приватном режиме поведение отличается в разных браузерах — в некоторых данные не сохраняются после закрытия вкладки. Размер квоты может варьироваться и зависеть от свободного места устройства.
Structured clone не сохраняет функции и некоторые специфические объекты. Также в браузере нет полноценного SQL-подобного языка запросов, поэтому сложные выборки реализуются через индексы и курсоры. При работе с бинарными файлами учитывайте влияние на квоту и производительность.
Ошибки, которые чаще всего встречаются
Первая ошибка — попытка открыть транзакцию после закрытия соединения или вне обработчика onsuccess. Вторая — неправильная миграция схемы, когда onupgradeneeded не учитывает старые версии и теряет данные. Третья — надежда на синхронную работу; многие забывают, что запросы асинхронны, и пытаются читать результат до завершения операции.
Лучше сразу обернуть API в промисы или использовать библиотеку-обёртку. Это уменьшает вероятность гонок и упрощает обработку ошибок.
Инструменты отладки и тестирования
Chrome DevTools показывает базы IndexedDB на вкладке Application. Там можно просматривать object store и индексы, удалять записи и экспортировать данные. Firefox и другие браузеры предоставляют похожие инструменты, но детали интерфейса отличаются.
Для тестирования в CI имеет смысл использовать mock-реализации, например fake-indexeddb. Это позволяет запускать юнит-тесты без реального браузера и контролировать состояние базы.
Библиотеки и полезные обёртки
Если не хочется писать множество промисов вручную, стоит обратить внимание на библиотеку idb. Она упрощает работу с транзакциями и делает код чище. Но иногда собственная обёртка под нужды приложения даёт лучший контроль и более тонкие оптимизации.
При выборе решения учитывайте сложность проекта. Для простых задач можно использовать минималистичные обёртки, а для крупных приложений — архитектуру с сервисом доступа к данным и централизованной синхронизацией.
Рекомендации и практические приёмы
Всегда планируйте версионирование схемы заранее: добавляйте индекс только при необходимости и записывайте миграции в виде кода, понятного другим разработчикам. Это избавит от неожиданных ошибок при обновлении.
Не держите огромные транзакции; разбивайте пакетные операции на небольшие части. Это снижает время блокировок и уменьшает риск таймаутов на мобильных устройствах.
- Используйте индексы для часто фильтруемых полей.
- Проверяйте квоты и очищайте устаревшие данные.
- Тестируйте миграции на копиях реальных данных.
- Оберните API в промисы или используйте idb.
IndexedDB — мощный инструмент, но он требует аккуратности. Простые интерфейсы и продуманная схема облегчают дальнейшую поддержку и масштабирование приложения.
Когда всё настроено правильно, вы получите надёжный способ хранения больших объёмов и гибкую платформу для офлайн-режима, кэширования и сложных клиентских операций. Это не магия, а грамотная архитектоника клиентской части приложения.

