Как стать автором
Обновить

Обертка для indexedDB / localStorage /…

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров2.5K

Библиотека storage-facade, о которой пойдет речь в этой статье, предоставляет единый синхронный / асинхронный API хранилища, являющийся абстракцией над реальной реализацией хранилища. Для конечного пользователя она упрощает использование любых хранилищ, для которых абстрактный класс из storage-facade будет реализован. Как автор этой библиотеки, расскажу о её использовании.

Есть реализации для IndexedDB, localStorage, sessionStorage, обёртка для Map.

Рассмотрим самый простой вариант, storage-facade-localstoragethin.

Установка

npm install storage-facade@4 storage-facade-localstoragethin@1

Использование

Вот такой код:

import { createStorage } from 'storage-facade';
import { LocalStorageThin } from 'storage-facade-localstoragethin';

const storage = createStorage({
  use: new LocalStorageThin(),
  useCache: true, // поддержка кеширования (мемоизации)
});

try {
  storage.Pen = { data: [40, 42] };
  storage.pineApple = 10;
  storage.apple = [1, 2, 3];
  storage.pen = 'Uh!';
} catch (e) {
  console.error((e as Error).message);
  // Если вы не используете TypeScript то замените на
  // console.error(e.message);
}

Приведёт к созданию следующих ключей в localStorage:

Эта магия реализована при помощи Proxy (MDN): мы перехватываем обращение к ключам объекта хранящегося в переменной storage, а так же операцию удаления ключей, например delete storage.pen;.

Объект хранилища предоставляет следующие методы:

  • .clear() - очищает хранилище

  • .entries() - возвращает массив пар ключ-значение

  • .deleteStorage() - удаляет хранилище (зависит от конкретной реализации, обычно сначала выполняется .clear(), а затем объект хранилища блокируется для чтения, записи и использования методов, выбрасывая ошибку при попытке доступа.

  • .size() - возвращает количество пар ключ-значение

  • .key(index: number) - возвращает имя ключа по его индексу

Кроме того, есть методы для работы с "дефолтными значениями". Дефолтные значения хранятся не в хранилище (в данном случае не в localStorage), а в экземпляре. Дефолтные значения используются, если хранилище при запросе ключа возвращает undefined.

Это удобно, мы можем задавать в коде дефолтное значение, например, для темы (тёмная или светлая), после чего по клику пользователя на кнопку, просто менять значение на противоположное. Если пользователь ещё не менял тему, то будет использовано дефолтное значение, если же он уже ранее менял тему, то будет использовано сохранённое в localStorage значение. Нам не нужно беспокоиться об этой логике.

  • .addDefault(obj) - добавляет ключи и значения переданного объекта к уже хранящимся в экземпляре

  • .setDefault(obj) - заменяет объект содержащий ключи и значения в экземпляре переданным пользователем

  • .getDefault() - возвращает объект, содержащий дефолтные ключи и значения

  • .clearDefault() - заменяет объект с дефолтными ключами и значениями пустым объектом

Вот пример, который должен прояснить использование дефолтных значений на практике:

import { createStorage } from 'storage-facade';
import { LocalStorageThin } from 'storage-facade-localstoragethin';

const storage = createStorage({
  use: new LocalStorageThin(),
  useCache: true,
});

try {
  // Такого ключа нет
  console.log(storage.value) // undefined

  // Добавим дефолтные значения
  storage.addDefault({ value: 9, other: 3 });
  // `1` перезапишет `9` в `value`
  storage.addDefault({ value: 1, value2: 2 });

  // Так как `storage.value = undefined`
  // то будет использовано дефолтное значение
  console.log(storage.value);  // 1
  // аналогично
  console.log(storage.value2); // 2
  console.log(storage.other);  // 3

  // Теперь установим значение
  storage.value = 42;
  // Когда мы установили значение отличное от `undefined`,
  // дефолтное значение больше не используется
  console.log(storage.value); // 42

  // Снова изменим на `undefined`
  storage.value = undefined;
  // используется дефолтное значение
  console.log(storage.value); // 1

  // `null` не приводит к использованию дефолтных значений
  storage.value = null;
  console.log(storage.value); // null

  // Удалим ключ из хранилища
  delete storage.value;
  // Теперь снова используется дефолтное значение
  console.log(storage.value); // 1

  // getDefault
  console.log(storage.getDefault()); // { value: 1, value2: 2, other: 3 }

  // Замена 'default'
  storage.setDefault({ value: 30 });

  // Тут выводится дефолтное значение `30` заданное строкой выше
  console.log(storage.value); // 30
  console.log(storage.value2); // undefined

  // clearDefault
  storage.clearDefault();

  // Так как дефолтные значения очищены,
  // мы больше не видим `30`
  console.log(storage.value); // undefined
  console.log(storage.value2); // undefined
} catch (e) {
  console.error((e as Error).message);
}

Ограничения

Мы можем перехватывать только ключи первого уровня, поэтому вот такой код сработает для чтения, но не сработает для записи:

  // Read
  // С чтением проблем нет
  console.log((storage.value as Record<string, unknown>).data); // Ok

  // Write
  // Не делайте так
  storage.value.data = 42; // Никакого эффекта

Вместо этого используйте следующий подход:

  // Read
  console.log((storage.value as Record<string, unknown>).data); // Ok

  // Write
  // Получаем объект
  const updatedValue = storage.value as Record<string, unknown>;
  // Вносим изменения
  updatedValue.data = 42;
  // Обновляем хранилище
  storage.value = updatedValue; // Ок 

Другие возможности

Есть расширенная версия этой библиотеки для localStoragestorage-facade-localstorage. Она позволяет создавать "виртуальные" хранилища, которые можно очищать не затрагивая данные в других виртуальных хранилищах и другие ключи (возможно от других библиотек), хранящихся в localStorage. Кроме того, можно обходить каждое отдельное хранилище при помощи метода .entries(). Цена за это – префиксы у ключей и хранение дополнительного ключа содержащего массив имен ключей для каждого виртуального хранилища.

Более подробная документация и ссылки на все реализованные на данный момент интерфейсы на странице библиотеки storage-facade.

Спасибо за внимание, хорошего дня!

Теги:
Хабы:
Всего голосов 3: ↑3 и ↓0+3
Комментарии5

Публикации

Истории

Работа

Ближайшие события

22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
11 – 13 декабря
Международная конференция по AI/ML «AI Journey»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань