localForage — Что делать если localStorage уже не хватает?
Как вы уже, наверное, знаете, браузерные хранилища данных, такие как localStorage и sessionStorage, сильно ограничены в своих размерах и для хранения большого количества данных не подходят. В разных браузерах этот размер варьируется, но в среднем принято считать, что это около 5 МБ.
Что же делать, если нужно больше? Для этой ситуации в браузере предусмотрен другой Web API — IndexedDB. Его размеры и регулирование также сильно варьируются от браузера к браузеру, но обычно это около 50% доступного дискового пространства на устройстве пользователя.
Сам этот API достаточно неудобен в использовании, так как предполагает работу с большим количеством объёмных конструкций в коде и без использования заранее подготовленных собственных утилит не представляется возможным.
К счастью, для нас существует большое количество библиотек, которые упрощают взаимодействие с этим хранилищем в разы. Вот некоторые из них:
Dexie.js
idb
localForage
Нас больше интересует последний вариант — localForage. Он в отличие от остальных библиотек реализует API очень близкий по реализации localStorage, что ещё больше упрощает работу для решения большинства задач.
Перед тем как мы перейдём к его подробному разбору, хотелось бы дополнительно отметить несколько дополнительных преимуществ перед стандартным localStorage, помимо увеличенного размера хранилища:
Асинхронный API — при частых операциях с данными наш поток не будет забиваться как в случае с localStorage, все задачи выполняются отложено.
Формат хранения данных — localForage поддерживает любой формат данных (объекты, массивы, бинарные данные и так далее) и не требует предварительной сериализации / десериализации данных, так как под капотом работает IndexedDB, который умеет это делать из коробки. Это удобно и не требует дополнительных вычислительных трат на преобразование.
Итак, приступим к разбору.
Установка
# Eсли у вас npm
npm install localforage
# Если у вас yarn
yarn add localforage
Также можно создать глобальную переменную при помощи ручной установки тега script в коде:
Основные методы
Библиотека предлагает 2 варианта обработки операций: при помощи колбэков и при помощи промисов. Далее на примере метода getItem можно будет увидеть их оба.
getItem
getItem(key, callback)
Получает содержимое хранилища по ключу key и передаёт его в callback.
Если ранее был сохранён undefined по определённому ключу, то при получении этого элемента при помощи getItem () будет возвращён null. Это сделано по причинам совместимости localForage и ограничений localStorage.
localforage.getItem('somekey').then((value) => {
console.log(value);
});
localforage.getItem('somekey', (error, value) => {
console.log(value);
});
setItem
setItem(key, value, callback)
Сохраняет данные value по заданному ключу key и вызывает callback.
localforage.setItem('somekey', 'some value').then((value) => {
console.log(value);
});
removeItem
removeItem(key, callback)
Удаляет данные по ключу key и вызывает callback.
localforage.removeItem('somekey').then(() => {
console.log('Ключ somekey был успешно удалён.');
});
clear
clear(callback)
Удаляет все ключи из базы данных.
localforage.clear().then(() => {
console.log('База данных была очищена.');
});
length
length(callback)
Возвращает количество записей в хранилище и вызывает callback.
localforage.length().then((numberOfKeys) => {
console.log(numberOfKeys);
});
key
key(keyIndex, callback)
Получает имя ключа по его индексу (порядковому номеру) и передаёт его в callback.
localforage.key(2).then((keyName) => {
console.log(keyName);
});
keys
keys(callback)
Возвращает список всех ключей хранилища и передаёт их в callback.
localforage.keys().then((keys) => {
console.log(keys);
});
iterate
iterate(iteratorCallback, callback)
Проходит по всем элементам в хранилище используя функцию iteratorCallback и возвращает результат в callback.
localforage.iterate((value, key, iterationNumber) => {
console.log([key, value]);
});
Драйверы
Под драйверами в библиотеке подразумеваются типы хранилищ, используемых для записи данных. По-умолчанию их 3: IndexedDB, WebSQL (устарел и не рекомендуется к использованию) и localStorage. Если явно не указать необходимый драйвер, то localForage автоматически его выберет на основании поддержки браузером того или иного API в следующем порядке:
IndexedDB
WebSQL
localStorage
Мы можем управлять этим поведением при помощи метода setDriver.
setDriver(driverName)
setDriver([driverName, nextDriverName])
// Устанавливаем один предпочтительный драйвер
localforage.setDriver(localforage.LOCALSTORAGE);
// Устанавливаем несколько драйверов на случай, если один или несколько из них не будут поддерживаться в браузере
localforage.setDriver([localforage.WEBSQL, localforage.INDEXEDDB]);
Также при помощи метода defineDriver мы можем объявлять свои собственные драйверы.
Конфигурация
Каждому хранилищу, создаваемому при помощи localForage можно задавать определённые настройки при помощи конфигурации.
localforage.config({
driver: localforage.INDEXEDDB, // Указывает, какой драйвер использовать. Может быть: localforage.LOCALSTORAGE, localforage.INDEXEDDB или localforage.WEBSQL
name: 'myApp', // Имя базы данных (используется для идентификации хранилища)
version: 1.0, // Версия базы данных
description: 'My App storage', // Описание базы данных
size: 4980736, // Размер хранилища в байтах для WebSQL (по умолчанию — 5MB)
});
Несколько хранилищ
Для работы сразу с несколькими хранилищами сразу мы можем использовать методы:
createInstance — для создания нового инстанса
dropInstance — для удаления существующего
// Создаем первый инстанс с уникальным именем 'userData'
const userDataStore = localforage.createInstance({
name: 'myApp',
storeName: 'userData',
driver: localforage.INDEXEDDB,
});
// Записываем в него какие-то данные
userDataStore.setItem('username', 'Вася').then(() => {
console.log('Данные пользователя сохранены');
});
// Создаем второй инстанс с уникальным именем 'appSettings'
const appSettingsStore = localforage.createInstance({
name: 'myApp',
storeName: 'appSettings',
driver: localforage.LOCALSTORAGE,
});
// Также записываем в него какие-то данные
appSettingsStore.setItem('theme', 'dark').then(() => {
console.log('Настройки приложения сохранены.');
});
// Удаляем их оба
userDataStore.dropInstance().then(() => {
console.log('userDataStore был удалён')
});
appSettingsStore.dropInstance().then(() => {
console.log('appSettingsStore был удалён')
});
Дополнительно
Думаю на данном этапе у вас уже сложилась картина как устроена библиотека. Методы, которые я не перечислил здесь, вы можете самостоятельно найти в официальной документации.