IndexedDB: клиентская база данных для сложных приложений
Что такое IndexedDB и зачем он нужен?
IndexedDB — это мощная клиентская база данных, которая позволяет хранить большие объёмы структурированных данных прямо в браузере. В отличие от localStorage, она поддерживает транзакции, индексы и даже сложные запросы! 🎯
Когда использовать:
- Для офлайн-режима в веб-приложениях 📱
- Кеширования данных для ускорения работы 🚀
- Хранения пользовательских настроек и истории 📊
- Работы с большими файлами (например, изображения или аудио) 🖼️
🔥 Важно: IndexedDB — асинхронная API. Это значит, что операции не блокируют основной поток, и интерфейс остаётся отзывчивым.
Основные концепции IndexedDB
1. Базы данных (Databases)
Каждая база данных: - Существует в рамках одного источника (origin) - Имеет имя и версию - Содержит одно или несколько хранилищ объектов (object stores)
2. Хранилища объектов (Object Stores)
Это как таблицы в SQL: - Хранят данные в виде пар ключ-значение - Могут иметь индексы для быстрого поиска - Поддерживают различные типы ключей (числа, строки, даты)
3. Индексы
Позволяют: - Быстро искать данные по свойствам, отличным от первичного ключа - Создавать уникальные или неуникальные индексы - Сортировать данные по определённым полям
Открытие базы данных
Создадим или откроем базу данных "NotesApp" версии 1:
const openRequest = indexedDB.open('NotesApp', 1);
openRequest.onerror = function(event) {
console.error('Database error:', event.target.error);
};
openRequest.onsuccess = function(event) {
const db = event.target.result;
console.log('Database opened successfully!');
// Теперь можно работать с базой данных
};
openRequest.onupgradeneeded = function(event) {
const db = event.target.result;
// Создаём хранилище объектов для заметок
const notesStore = db.createObjectStore('notes', {
keyPath: 'id',
autoIncrement: true
});
// Создаём индекс для поиска по заголовку
notesStore.createIndex('by_title', 'title', { unique: false });
console.log('Database structure updated!');
};
💡 Совет: Версия базы данных важна! При её изменении срабатывает
onupgradeneeded, где можно обновить схему данных.
CRUD-операции в IndexedDB
Создание записи (Create)
function addNote(db, note) {
const transaction = db.transaction('notes', 'readwrite');
const store = transaction.objectStore('notes');
const request = store.add(note);
request.onsuccess = function() {
console.log('Note added with ID:', request.result);
};
request.onerror = function(event) {
console.error('Error adding note:', event.target.error);
};
}
Чтение записи (Read)
function getNote(db, id) {
return new Promise((resolve, reject) => {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
const request = store.get(id);
request.onsuccess = function() {
resolve(request.result);
};
request.onerror = function(event) {
reject(event.target.error);
};
});
}
Обновление записи (Update)
function updateNote(db, id, updates) {
const transaction = db.transaction('notes', 'readwrite');
const store = transaction.objectStore('notes');
// Сначала получаем запись
const getRequest = store.get(id);
getRequest.onsuccess = function() {
const note = getRequest.result;
if (!note) return;
// Обновляем поля
Object.assign(note, updates);
// Сохраняем обратно
const putRequest = store.put(note);
putRequest.onsuccess = function() {
console.log('Note updated successfully');
};
};
}
Удаление записи (Delete)
function deleteNote(db, id) {
const transaction = db.transaction('notes', 'readwrite');
const store = transaction.objectStore('notes');
const request = store.delete(id);
request.onsuccess = function() {
console.log('Note deleted');
};
request.onerror = function(event) {
console.error('Error deleting note:', event.target.error);
};
}
Работа с индексами и сложные запросы
Поиск по индексу
function searchByTitle(db, title) {
return new Promise((resolve, reject) => {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
const index = store.index('by_title');
const request = index.getAll(title);
request.onsuccess = function() {
resolve(request.result);
};
request.onerror = function(event) {
reject(event.target.error);
};
});
}
Получение всех записей
function getAllNotes(db) {
return new Promise((resolve, reject) => {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
const request = store.getAll();
request.onsuccess = function() {
resolve(request.result);
};
request.onerror = function(event) {
reject(event.target.error);
};
});
}
Использование курсоров
function iterateNotes(db, callback) {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
const request = store.openCursor();
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
callback(cursor.value);
cursor.continue();
}
};
}
Лучшие практики работы с IndexedDB
-
Всегда закрывайте соединения
Когда закончили работу, вызовитеdb.close(). Это освобождает ресурсы. -
Используйте транзакции правильно
Транзакции завершаются автоматически после обработки всех запросов. Не делайте между запросами долгих операций. -
Обрабатывайте ошибки
IndexedDB операции могут завершиться неудачно. Всегда добавляйте обработчикиonerror. -
Оптимизируйте структуру данных
Продумывайте индексы заранее — их нельзя изменить без обновления версии базы. -
Используйте промисы или async/await
Оберните операции в промисы для удобства работы:
async function getNoteAsync(db, id) {
const transaction = db.transaction('notes', 'readonly');
const store = transaction.objectStore('notes');
return new Promise((resolve, reject) => {
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = (event) => reject(event.target.error);
});
}
Когда IndexedDB — не лучший выбор?
Несмотря на мощь, IndexedDB подходит не для всех сценариев:
- Для простых данных — используйте
localStorageилиsessionStorage - Для работы с сервером — лучше подойдут серверные базы данных
- Для мультимедиа — иногда проще использовать Cache API
Но для сложных клиентских приложений, требующих офлайн-работы и структурированного хранения данных, IndexedDB — идеальное решение! 🎉