Размер объектов в хранилище Dexie.js напрямую определяется тем, какие данные попадают в IndexedDB и в каком виде они сериализуются. JavaScript-объекты сохраняются через structured clone algorithm, поэтому избыточные поля, вложенные структуры и дублирование мгновенно увеличивают объём базы.
Ключевая стратегия уменьшения размера заключается в переходе от хранения «удобных объектов приложения» к хранению «оптимизированных записей хранилища».
Во многих моделях данных присутствуют поля, которые нужны только на уровне UI:
Такие поля не должны попадать в IndexedDB.
function prepareUserForStorage(user) {
return {
id: user.id,
name: user.name,
avatarId: user.avatarId,
createdAt: user.createdAt
};
}
Минимизация структуры снижает не только размер, но и стоимость индексации.
Вложенные структуры JSON являются одной из основных причин разрастания базы.
Пример неэффективного хранения:
{
id: 1,
name: "Order",
items: [
{ productId: 10, title: "Keyboard", price: 100 },
{ productId: 11, title: "Mouse", price: 50 }
]
}
Каждый заказ дублирует данные товаров, что приводит к экспоненциальному росту объёма.
Нормализованный вариант:
// Orders
{
id: 1,
itemIds: [10, 11]
}
// Products
{
id: 10,
title: "Keyboard",
price: 100
}
Dexie.js хорошо работает с такой схемой через несколько таблиц и
последующую агрегацию через join на уровне приложения.
const db = new Dexie("shop");
db.version(1).stores({
orders: "id, date",
products: "id, title"
});
Размер данных зависит не только от объектов, но и от структуры индексов. Каждый индекс в IndexedDB хранит дополнительную копию значений.
Каждое поле, добавленное в stores(), увеличивает расход
памяти:
db.version(1).stores({
users: "id, email, phone, city, country, createdAt"
});
Если поле не используется для поиска, его индексация неоправданна.
Оптимизированный вариант:
db.version(1).stores({
users: "id, email, city"
});
Остальные поля остаются в объекте, но не индексируются.
Чрезмерное количество отдельных индексов иногда заменяется одним составным:
db.version(1).stores({
logs: "id, [userId+createdAt]"
});
Это уменьшает количество индексных структур при сохранении функциональности фильтрации.
IndexedDB хранит данные в сериализованном виде, поэтому каждый байт структуры имеет значение.
Длинные имена свойств увеличивают размер каждой записи:
{
userIdentifier: 123,
creationTimestamp: 1710000000
}
Оптимизировано:
{
uid: 123,
ts: 1710000000
}
На больших объёмах данных разница становится существенной.
Dexie.js не выполняет компрессию автоматически, поэтому при необходимости её реализуют вручную.
Подход заключается в сериализации объекта в строку и последующем сжатии.
import { gzip, ungzip } fr om "pako";
function compress(data) {
return gzip(JSON.stringify(data));
}
function decompress(binary) {
return JSON.parse(ungzip(binary, { to: "string" }));
}
Хранение:
await db.blobs.put({
id: 1,
data: compress(largeObject)
});
Такой подход особенно эффективен для:
Перевод бинарных данных в Base64 увеличивает размер примерно на 33%, поэтому используется только при необходимости совместимости.
JSON не всегда является оптимальным форматом хранения.
Для изображений, файлов и медиа лучше хранить бинарные данные напрямую:
await db.files.put({
id: "img1",
data: arrayBuffer
});
Dexie.js и IndexedDB эффективно работают с Blob и
ArrayBuffer, избегая лишней сериализации.
Очень крупные структуры целесообразно разделять на чанки.
Вместо хранения всех версий в одном объекте:
{
id: 1,
versions: [ ...очень большой массив... ]
}
используется разбиение:
{
docId: 1,
versionId: 1,
data: {...}
}
Индексация по docId позволяет собирать данные по мере
необходимости.
db.versions.wh ere("docId").equals(1).toArray();
Строки часто занимают основную часть объёма базы.
Если данные содержат повторяющиеся строки (статусы, категории, роли), их выгодно выносить в справочники:
// вместо:
{
id: 1,
status: "processing"
}
// лучше:
{
id: 1,
statusId: 2
}
Справочник:
{
id: 2,
name: "processing"
}
Каждый уровень вложенности увеличивает стоимость хранения и чтения.
{
id: 1,
user_city: "Almaty",
user_country: "KZ"
}
вместо:
{
id: 1,
user: {
location: {
city: "Almaty",
country: "KZ"
}
}
}
Плоская модель также упрощает индексацию в Dexie.
Объекты Date сериализуются с дополнительными накладными
расходами.
{
createdAt: Date.now()
}
вместо:
{
createdAt: new Date()
}
Числовое представление компактнее и быстрее индексируется.
Кэш часто становится основной причиной разрастания IndexedDB.
Плохой вариант:
{
page1: { user: {...}, posts: [...] },
page2: { user: {...}, posts: [...] }
}
Лучше:
{
userId: 1,
postIds: [10, 11, 12]
}
И отдельные таблицы для сущностей.
Хранение всех данных сразу увеличивает объём, даже если часть данных никогда не используется.
{
id: 1,
title: "Article",
contentRef: "chunk_1"
}
Тело статьи загружается только при необходимости.
Размер базы часто растёт из-за отсутствия стратегии очистки.
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
await db.logs.where("timestamp").below(cutoff).delete();
Регулярная очистка снижает накопление «мусорных» данных, которые не участвуют в бизнес-логике, но занимают место и индексы.
Сильная минимизация структуры не всегда приводит к оптимальному результату. Иногда нормализация увеличивает количество чтений, а сжатие — нагрузку на CPU.
Практический баланс достигается через:
Такой подход позволяет удерживать размер IndexedDB под контролем без деградации скорости операций Dexie.js.