localForage позволяет сохранять сложные структуры данных напрямую, без ручной сериализации в строку. Объекты и массивы в этом контексте становятся основным рабочим типом данных, поскольку библиотека ориентирована на хранение не только примитивов, но и вложенных структур, характерных для прикладных веб-приложений.
При записи объекта в хранилище используется тот же интерфейс, что и для строк или чисел. Разница заключается в том, что объект передаётся напрямую, а сериализация выполняется внутри библиотеки.
import localForage from "localforage";
const user = {
id: 1,
name: "Alex",
settings: {
theme: "dark",
notifications: true
}
};
await localForage.setItem("user", user);
При чтении данные возвращаются в виде исходной структуры:
const savedUser = await localForage.getItem("user");
console.log(savedUser.name);
console.log(savedUser.settings.theme);
Ключевой момент заключается в том, что localForage
использует механизм сериализации, зависящий от выбранного драйвера. В
большинстве современных браузеров это IndexedDB, где применяется
структурированное клонирование (structured clone algorithm), позволяющее
сохранять вложенные объекты без потери структуры.
Массивы в localForage используются для хранения коллекций сущностей: списков задач, истории действий, кэша запросов.
const tasks = [
{ id: 1, title: "Buy milk", done: false },
{ id: 2, title: "Read book", done: true }
];
await localForage.setItem("tasks", tasks);
Чтение массива возвращает полноценную коллекцию объектов:
const storedTasks = await localForage.getItem("tasks");
storedTasks.forEach(task => {
console.log(task.title);
});
Массивы сохраняются как единая структура, а не как набор отдельных записей. Это означает, что операции частичного обновления невозможны напрямую — всегда перезаписывается весь массив целиком.
localForage корректно обрабатывает вложенные структуры любой глубины, пока они совместимы с возможностями структурированного клонирования.
const state = {
profile: {
id: 10,
meta: {
visitedPages: ["/home", "/profile"]
}
},
cache: {
posts: [
{ id: 1, content: "Hello" },
{ id: 2, content: "World" }
]
}
};
await localForage.setItem("appState", state);
Такие данные возвращаются в исходной форме, что делает localForage удобным для хранения состояния приложения целиком.
Поведение хранения объектов и массивов зависит от используемого драйвера:
Из-за этого возможны различия:
const data = {
created: new Date()
};
await localForage.setItem("dateTest", data);
В IndexedDB Date сохраняется как объект Date. В
localStorage fallback — превращается в строку.
Это критично учитывать при разработке кроссбраузерных решений, где возможен переход между драйверами.
На практике чаще всего используется структура «массив объектов», которая имитирует таблицу.
const users = await localForage.getItem("users") || [];
users.push({
id: 3,
name: "Maria"
});
await localForage.setItem("users", users);
Такой подход имеет важное ограничение: отсутствует встроенная система индексации. Поиск и обновление выполняются вручную:
const users = await localForage.getItem("users") || [];
const updatedUsers = users.map(user =>
user.id === 3 ? { ...user, name: "Maria Ivanova" } : user
);
await localForage.setItem("users", updatedUsers);
Это делает localForage ближе к файловому хранилищу, чем к базе данных.
Данные, полученные из localForage, не связаны с внутренним состоянием хранилища. Однако логически они представляют снимок на момент чтения. Любые изменения требуют повторной записи.
const state = await localForage.getItem("state");
state.counter += 1;
await localForage.setItem("state", state);
Отсутствие автоматического синхронизированного обновления означает необходимость явного управления состоянием, особенно при конкурентных изменениях.
При работе с крупными массивами важно учитывать, что localForage сохраняет весь объект целиком. Это влияет на:
Пример проблемного сценария:
const logs = new Array(100000).fill(0).map((_, i) => ({
id: i,
message: "event"
}));
await localForage.setItem("logs", logs);
В таких случаях более эффективным решением становится разбиение данных на части:
await localForage.setItem("logs_page_1", chunk1);
await localForage.setItem("logs_page_2", chunk2);
При хранении сложных объектов важно учитывать, что не все типы данных сохраняются одинаково:
undefined игнорируется или теряетсяconst invalid = {
fn: () => {},
value: undefined
};
await localForage.setItem("test", invalid);
После восстановления объект будет изменён, поскольку функции не поддерживаются структурированным клонированием.
При изменении вложенных объектов рекомендуется использовать копирование, чтобы избежать случайной мутации:
const state = await localForage.getItem("state");
const newState = {
...state,
profile: {
...state.profile,
theme: "light"
}
};
await localForage.setItem("state", newState);
Такой подход обеспечивает предсказуемость и предотвращает частичные изменения структуры, которые могут привести к логическим ошибкам.
localForage позволяет комбинировать массивы и объекты в любых конфигурациях:
const dataset = {
users: [
{ id: 1, roles: ["admin", "editor"] },
{ id: 2, roles: ["viewer"] }
],
metadata: {
version: 1,
tags: ["core", "auth"]
}
};
await localForage.setItem("dataset", dataset);
Такие структуры особенно полезны при хранении состояния SPA-приложений, где требуется сохранить целостный снимок данных без нормализации.
На уровне архитектуры localForage чаще всего используется в трёх сценариях:
Во всех случаях ключевой принцип заключается в том, что объект или массив рассматривается как единый атомарный блок данных, который читается и записывается целиком, без частичных обновлений на уровне хранилища