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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Библиотека 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; // Ок 

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

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

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

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

Источник: https://habr.com/ru/articles/759200/


Интересные статьи

Интересные статьи

localStorage для авторизации в React приложении
Привет! В этой статье я кратко объясню что такое localStorage в JavaScript, его особенности и расскажу как с ним работать.
В 2020 году автор оригинальной статьи решил написать более эффективную библиотеку PJAX, похожую на проект Rezo Zero — Starting Blocks, но так, чтобы легко ...
Допустим, у нас есть бекенд, который умеет хранить какие-то сущности. И у него есть апи для создания, чтения, изменения и удаления этих сущностей, сокращенно CRUD. Но апи на сервере, а по...
Подошло время рассказать как была добавлена поддержка поддержка российской криптографии в проект PyKCS11. Всё началось с того, что мне на глаза попалась переписка разработчика проекта Py...