Схема в Dexie.js определяет структуру IndexedDB-базы: набор таблиц (object stores), их ключи, индексы и правила эволюции данных. Именно через схему задаётся контракт хранения, который сохраняется между версиями приложения и обновлениями структуры базы.
Dexie.js использует декларативный подход: схема описывается строкой или массивом строк внутри версии базы данных, после чего библиотека синхронизирует её с реальным состоянием IndexedDB.
Каждое изменение структуры базы фиксируется через инкремент версии:
version(n)) описывает состояние схемы на
момент релиза;Принцип работы:
Ключевая особенность: схема не изменяется «на месте», а всегда привязана к версии.
Схема задаётся через метод stores:
const db = new Dexie("AppDatabase");
db.version(1).stores({
users: "++id, name, email, age",
posts: "++id, title, userId, createdAt"
});
Структура:
users, posts — имена таблиц (object
stores);Первичный ключ задаётся первыми символами в строке схемы.
Основные варианты:
++id
++ означает автоувеличение;id — имя поля.id
Используется, когда значение задаётся вручную.
[id+type]
Используется для составных уникальных идентификаторов.
Пример:
orders: "[userId+orderId], userId, orderId, createdAt"
Dexie.js поддерживает несколько типов индексов:
name, email, age
Каждое поле после первичного ключа становится индексируемым.
&email
Оператор & накладывает уникальность:
users: "++id, &email, name"
Это гарантирует отсутствие повторяющихся значений email.
*tags
Позволяет индексировать массив:
posts: "++id, title, *tags"
Если tags = ["js", "indexeddb"], создаются отдельные
записи в индексе для каждого элемента массива.
userId, createdAt
или более сложный вариант:
[userId+createdAt]
Разница:
[] — единый композитный индекс.Схема Dexie.js представляет собой строку с синтаксисом:
[primaryKey], index1, index2, ..., indexN
Специальные модификаторы:
++ — автоинкремент;& — уникальный индекс;* — multiEntry индекс;[a+b] — композитный ключ.Пример комбинированной схемы:
db.version(1).stores({
products: "++id, &sku, name, category, *tags, [category+name]"
});
В одной версии можно определить множество stores:
db.version(1).stores({
users: "++id, &email, name",
orders: "++id, userId, createdAt",
orderItems: "++id, orderId, productId"
});
Каждая таблица имеет независимую схему, но разделяет версионный контекст базы.
Изменение структуры выполняется через новую версию:
db.version(1).stores({
users: "++id, name, email"
});
db.version(2).stores({
users: "++id, name, email, phone"
});
При переходе:
Важно: удаление индекса требует его исключения из новой схемы.
При изменении структуры данных используется upgrade:
db.version(2).stores({
users: "++id, name, email, phone"
}).upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
user.phone = "";
});
});
Характеристики:
IndexedDB не поддерживает прямое переименование, поэтому Dexie.js требует ручной миграции:
db.version(2).stores({
customers: "++id, fullName"
}).upgrade(async tx => {
const oldUsers = tx.table("users");
const newCustomers = tx.table("customers");
await oldUsers.each(user => {
newCustomers.add({
id: user.id,
fullName: user.name
});
});
});
Удаление осуществляется через исключение из схемы новой версии:
db.version(2).stores({
users: "++id, name"
});
Если ранее существовал индекс email, он будет удалён
автоматически.
Для удаления всей таблицы:
stores;Схема наследует ограничения IndexedDB:
Dexie.js компенсирует это удобной декларацией и API миграций.
Каждая сущность описывается отдельной таблицей:
users
posts
comments
Индексы создаются не по структуре данных, а по паттернам доступа:
Композитные ключи используются только при необходимости строгой уникальности.
Схема Dexie.js не является статичной структурой. Она представляет последовательность состояний:
Каждое состояние полностью определяет структуру базы в конкретный момент времени, что исключает неопределённость при обновлениях приложения.