Объекты и массивы

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 удобным для хранения состояния приложения целиком.

Ограничения драйверов и различия поведения

Поведение хранения объектов и массивов зависит от используемого драйвера:

  • IndexedDB — поддерживает сложные объекты, Map, Set, Date, ArrayBuffer
  • WebSQL — менее предсказуем, но сохраняет объекты через сериализацию
  • localStorage — работает через JSON.stringify / JSON.parse

Из-за этого возможны различия:

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 сохраняет весь объект целиком. Это влияет на:

  • время сериализации/десериализации
  • нагрузку на память
  • блокировки записи в IndexedDB транзакциях

Пример проблемного сценария:

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 чаще всего используется в трёх сценариях:

  • хранение состояния приложения целиком
  • кэширование API-ответов (объекты и списки)
  • локальные коллекции данных без серверной синхронизации

Во всех случаях ключевой принцип заключается в том, что объект или массив рассматривается как единый атомарный блок данных, который читается и записывается целиком, без частичных обновлений на уровне хранилища