Метод version().upgrade() в Dexie.js предназначен для
выполнения миграционного кода при переходе между версиями базы данных.
Он позволяет явно определить логику преобразования данных, когда
изменяется схема, индексы или структура хранимых объектов.
Каждое изменение версии базы данных в Dexie сопровождается
потенциальной несовместимостью старых данных с новой схемой. Описание
схемы через version(n).stores({...}) фиксирует структуру
таблиц, но не решает задачу преобразования уже существующих записей.
Метод upgrade() закрывает этот разрыв, предоставляя
возможность:
Метод вызывается в цепочке описания версии:
db.version(2)
.stores({
users: "++id,name,age"
})
.upgrade(tx => {
// миграционная логика
});
Общая форма:
db.version(n)
.stores(schema)
.upgrade(callback);
Где callback получает доступ к транзакции текущей
версии.
Функция, передаваемая в upgrade(), выполняется:
Контекст выполнения содержит доступ ко всем таблицам текущей версии
через db.tableName.
Пример доступа к таблице:
db.version(2)
.stores({
users: "++id,name,fullName"
})
.upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
user.fullName = user.name;
delete user.name;
});
});
При обновлении версии происходит последовательность шагов:
stores().upgrade().Если upgrade() выбрасывает исключение или возвращает
отклонённый Promise, вся миграция откатывается.
Основной сценарий — модификация существующих записей через коллекции.
db.version(2)
.stores({
users: "++id,firstName,lastName,fullName"
})
.upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
user.fullName = `${user.firstName} ${user.lastName}`;
});
});
Метод modify() изменяет записи пакетно, что важно для
больших таблиц.
db.version(2)
.stores({
users: "++id,name"
})
.upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
delete user.tempField;
});
});
db.version(2)
.stores({
logs: "++id,type,timestamp"
})
.upgrade(tx => {
return tx.table("logs")
.where("type")
.equals("debug")
.delete();
});
Функция upgrade() может возвращать Promise, что
позволяет выполнять цепочки асинхронных операций:
db.version(2)
.stores({
users: "++id,name,normalized"
})
.upgrade(async tx => {
const users = await tx.table("users").toArray();
await tx.table("users").clear();
await tx.table("users").bulkAdd(
users.map(u => ({
...u,
normalized: u.name.toLowerCase()
}))
);
});
При использовании async/await транзакция остаётся активной до завершения Promise.
При переходе сразу через несколько версий (например, с 1 на 4) Dexie
выполняет все промежуточные upgrade() последовательно:
Каждый шаг получает собственный транзакционный контекст своей версии.
Внутри upgrade() действуют ограничения IndexedDB:
db.version(2)
.stores({
users: "++id,first_name,last_name"
})
.upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
user.first_name = user.firstName;
user.last_name = user.lastName;
delete user.firstName;
delete user.lastName;
});
});
db.version(2)
.stores({
users: "++id,email,emailDomain"
})
.upgrade(tx => {
return tx.table("users").toCollection().modify(user => {
user.emailDomain = user.email.split("@")[1];
});
});
db.version(2)
.stores({
orders: "++id,itemsCount"
})
.upgrade(tx => {
return tx.table("orders").toCollection().modify(order => {
order.itemsCount = order.items.length;
});
});
Если миграция прерывается:
Типичные причины ошибок:
При работе с большими наборами данных критично учитывать:
toCollection().modify() эффективнее по памяти, чем
ручная итерация;bulkAdd() быстрее последовательных
add();modify() дешевле полной
пересборки таблицы;При значительном изменении структуры данных предпочтительно: