Idb-keyval — это минималистичная обёртка над IndexedDB, которая позволяет работать с ключ-значение в браузере. Одним из важных аспектов управления данными является версионирование — процесс сохранения нескольких состояний данных, чтобы можно было отслеживать изменения, откатываться к предыдущим версиям или синхронизировать обновления между клиентами.
Версионирование данных в Idb-keyval напрямую не встроено, поэтому для его реализации требуется создание собственной схемы хранения и управления версиями.
Для реализации версионирования необходимо:
Использовать ключи с версионной меткой.
Например, для объекта user можно формировать ключи
вида:
import { set, get } from 'idb-keyval';
const key = 'user:v1';
await set(key, { name: 'Alice', age: 25 });
Здесь v1 указывает на версию данных. При обновлении
данных создаётся новая версия:
const newKey = 'user:v2';
await set(newKey, { name: 'Alice', age: 26 });
Такой подход позволяет хранить все предыдущие версии без удаления старых данных.
Создать отдельный индекс версий, если планируется частый доступ к последней версии. Для этого можно хранить текущую версию в отдельном ключе:
await set('user:current', 'v2');
При чтении последней версии:
const currentVersion = await get('user:current');
const userData = await get(`user:${currentVersion}`);Для упрощения работы с версиями полезно создавать вспомогательные функции:
import { get, set } from 'idb-keyval';
async function saveVersionedData(baseKey, data) {
const currentVersion = await get(`${baseKey}:current`) || 'v0';
const versionNumber = parseInt(currentVersion.slice(1)) + 1;
const newVersion = `v${versionNumber}`;
await set(`${baseKey}:${newVersion}`, data);
await set(`${baseKey}:current`, newVersion);
return newVersion;
}
async function getLatestVersion(baseKey) {
const currentVersion = await get(`${baseKey}:current`);
if (!currentVersion) return null;
return await get(`${baseKey}:${currentVersion}`);
}
Эти функции позволяют централизованно управлять версиями, избегая дублирования логики в разных частях приложения.
Если приложение хранит большое количество версий, важно контролировать их количество, чтобы не переполнять IndexedDB. Это можно реализовать с помощью функции очистки старых версий:
import { get, keys, del } from 'idb-keyval';
async function pruneOldVersions(baseKey, maxVersions = 5) {
const allKeys = await keys();
const versionKeys = allKeys
.filter(k => typeof k === 'string' && k.startsWith(`${baseKey}:v`))
.sort((a, b) => {
const aNum = parseInt(a.split(':v')[1]);
const bNum = parseInt(b.split(':v')[1]);
return bNum - aNum;
});
const keysToDelete = versionKeys.slice(maxVersions);
for (const key of keysToDelete) {
await del(key);
}
}
Такой механизм позволяет хранить только последние N
версий, экономя пространство в IndexedDB.
Версионирование также позволяет реализовать откат к предыдущим состояниям. Для этого достаточно изменить текущую версию на одну из старых:
async function rollback(baseKey, targetVersion) {
const exists = await get(`${baseKey}:${targetVersion}`);
if (!exists) throw new Error('Версия не найдена');
await set(`${baseKey}:current`, targetVersion);
return exists;
}
Откат эффективен при необходимости восстановления данных после ошибок или некорректного обновления.
Версионирование удобно использовать не только для простых объектов, но и для массивов, списков задач, конфигураций приложений. Важно соблюдать единый формат ключей и хранить текущую версию отдельно, чтобы обеспечить быстрый доступ к актуальному состоянию.
Пример хранения истории изменений списка:
await saveVersionedData('tasks', [
{ id: 1, title: 'Написать статью', done: false },
]);
await saveVersionedData('tasks', [
{ id: 1, title: 'Написать статью', done: true },
]);
v1, v2) или timestamp
(v20260324T1015) для точного отслеживания изменений.Версионирование данных в Idb-keyval строится на трёх базовых элементах:
Следуя этим принципам, можно реализовать гибкую систему хранения, способную поддерживать историю изменений, откаты и безопасные обновления данных без сложных внешних библиотек.