indexeddb что это за папка

Готовим IndexedDB

На Хабре уже рассказывали про IndexedDB — стандарт хранения больших структурированных данных на клиенте. Но это было давно и API сильно изменился. Несмотря на это в поиске статья всплывает одной из первых и вводит в заблуждение многих, кто начинает пытатся работать с этой технологией. Поэтому я решил написать новую статью с информацией об актуальном API.

Что такое IndexedDB

IndexedDB — это объектная база данных, которая намного мощнее, эффективнее и надежней, чем веб-хранилище пар ключ/значение, доступное посредством прикладного интерфейса Web Storage. Как и в случае прикладных интерфейсов к веб-хранилищам и файловой системе, доступность базы данных определяется происхождением создавшего ее документа.

Для каждого происхождения может быть создано произвольное число баз данных IndexedDB. Каждая база данных имеет имя, которое должно быть уникальным для данного происхождения. С точки зрения прикладного интерфейса IndexedDB база данных является простой коллекцией именованных хранилищ объектов. Каждый объект должен иметь ключ, под которым он сохраняется и извлекается из хранилища. Ключи должны быть уникальными и они должны иметь естественный порядок следования, чтобы их можно было сортировать. IndexedDB может автоматически генерировать уникальные ключи для каждого объекта, добавляемого в базу данных. Однако, часто объекты, сохраняемые в хранилище объектов, уже будут иметь свойство, пригодное для использования в качестве ключа. В этом случае при создании хранилища объектов достаточно просто определить ключевое свойство.

Помимо возможности извлекать объекты из хранилища по значению первичного ключа существует также возможность выполнять поиск по значениям других свойств объекта. Чтобы обеспечить эту возможность, в хранилище объектов можно определить любое количество индексов. Каждый индекс определяет вторичный ключ хранимых объектов. Эти индексы в целом могут быть неуникальными, и одному и тому же ключу может соответствовать множество объектов. Поэтому в операциях обращения к хранилищу объектов с использованием индекса обычно используется курсор, определяющий прикладной интерфейс для извлечения объектов из потока результата по одному.

IndexedDB гарантирует атомарность операций: операции чтения и записи в базу данных объединяются в транзакции, благодаря чему либо они все будут успешно выполнены, либо ни одна из них не будет выполнена, и база данных никогда не останется в неопределенном, частично измененном состоянии.

Приступим

IE > 9, Firefox > 15 и Chrome > 23 поддерживают работу без префиксов, но все-таки лучше проверять все варианты:

Подключение к базе данных

Работа с базой данных начинается с запроса на открытие:

Onerror будет вызван в случае возникновения ошибки и получит в параметрах объект ошибки.

Onsuccess будет вызван если все прошло успешно, но экземпляр открытой базы данных в качестве параметра метод не получит. Открытая БД доступна из объекта запроса: request.result.

Пока все было как и раньше, но теперь начинаются отличия. Вторым аргументом методу open передается версия базы данных. Версией может быть только натуральное число. Если передать дробное, то оно будет округлено до целого. Если базы с указанной версией не найдется, то будет вызван onupgradeneeded, в котором можно модифицировать базу, если существует старая версия, или создать базу, если ее вообще не существует.

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

где f — это функция, которой будет передана открытая база данных.

Структура базы данных

IndexedDB оперирует не таблицами, а хранилищами объектов: ObjectStore. При создании ObjectStore можно указывать его имя и параметры: имя ключевого поля (строковое свойство объекта настроек: keyPath) и автогенерацию ключа (булево свойство объекта настроек: autoIncrement).

Создавать ObjectStore можно с помощью метода createObjectStore. При создании ObjectStore можно указать его имя и параметры, например, ключевое поле. Индекс базы данных можно создавать с помощью метода createIndex. При создании индекса можно указать его имя, поле по которому его необходимо построить, и параметры, например, уникальность ключа:

Работа с записями

Как уже говорилось во введении, любые операции с записями в IndexedDB происходят в рамках транзакции. Транзакция открывается методом transaction. В методе необходимо указать какие ObjectStore вам нужны и режим доступа: чтение, чтение и запись, смена версии. Режим смены версии по сути аналогичен методу onupgradeneeded.

Конкретные цифры не замерял, но думаю с точки зрения производительности лучше внимательно подходить к выставлению параметров транзакции: открывать только нужные вам ObjectStore и не просить запись, когда вам достаточно только чтения.

Курсор

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

Но ребятам из Mozilla, как и мне, такой способ получения всех записей показался неудобным и они сделали метод который сразу возвращает все содержимое ObjectStore: mozGetAll. Надеюсь, в будущем и остальные браузеры его реализуют.

Индекс

Если вы хотите получить значение используя индекс, то все тоже довольно просто:

Ограничения

Размер

По размеру ограничений почти что нет. Firefox ограничивает только размерами жесткого диска, но при условии, что на каждые дополнительные 50 мегабайт потребуется подтверждение пользователя. Chrome может занять под базы данных всех веб-страниц, которые их создали, половину жесткого диска, при этом ограничивая каждую базу данных 20% от этой половины.

Поддержка браузерами

Поддерживается всеми новыми браузерами кроме сафари и мобильной оперы, что, по-моему, не беда.

Пример

Обычно во всех статьях в качестве примера рассматривают адресную книгу, но я решил для разнообразия показать хранение файлов, хотя разницы по сути никакой и код в целом универсальный.

Заключение

IndexedDB уже в полной мере поддерживается браузерами и готово к употреблению. Это прекрасный инструмент для создания автономных веб-приложений, но использовать его нужно все-таки с умом. Где можно обойтись WebStorage — лучше обойтись WebStorage. Где можно ничего не хранить на клиенте, лучше ничего не хранить на клиенте.

Сейчас становится все больше библиотек, которые инкапсулируют внутри себя работу с WebStorage, FileSystem API, IndexedDB и WebSQL, но, по-моему, лучше написать хотя бы раз свой код, чтобы потом не тащить, когда не нужно, кучу чужого кода без понимания его работы.

Источник

Как использовать IndexDB для управления состоянием в JavaScript

Эта статья — перевод оригинальной статьи Craig Buckler «How to Use IndexDB to Manage State in JavaScript».

Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

В этой статье объясняется, как использовать IndexedDB для хранения состояния в типичном клиентском приложении на JavaScript.

Код доступен на Github. Он представляет собой пример to-do приложения, которое вы можете использовать или адаптировать для своих собственных проектов.

Что я имею ввиду под «состоянием»?

Системы управления состоянием, такие как Redux и Vuex, предоставляют централизованные хранилища данных. Любой компонент JavaScript может читать, обновлять или удалять данные. Некоторые системы позволяют компонентам подписываться на события изменений. Например, когда пользователь переключает светлый/темный режим, все компоненты соответственно обновляют свои стили.

Читайте также:  какой номер паспорта указывать на шейне

Большинство систем управления состоянием хранят значения в памяти, хотя доступны техники и плагины для передачи данных в localStorage, cookie и т. д.

Подходит ли IndexedDB для хранения состояния?

Как всегда: зависит от обстоятельств.

IndexedDB предлагает некоторые преимущества:

Обычно он может хранить 1 ГБ данных, что делает его подходящим для больших объектов, файлов, изображений и т. д. Перемещение этих элементов из памяти может сделать приложение более быстрым и эффективным.

В отличие от cookie и веб-хранилища (localStorage и sessionStorage), IndexedDB хранит данные в виде нативных объектов JavaScript. Нет необходимости сериализовать в строки JSON и потом снова парсить в объект.

Доступ к IndexedDB является асинхронным, поэтому он оказывает минимальное влияние на основной поток обработки JavaScript.

Обратите внимание, что localStorage и sessionStorage являются синхронными: ваш код JavaScript приостанавливает выполнение, пока он обращается к данным. Это может вызвать проблемы с производительностью при сохранении больших наборов данных.

Асинхронный доступ к данным имеет ряд недостатков:

API IndexedDB использует старый подход с коллбэками и событиями, поэтому библиотека-обёртка на основе промисов будет лучшим решением.

Асинхронные конструкторы классов и Proxy get/set невозможны в JavaScript. Это создает некоторые проблемы для систем управления состоянием.

Создание системы управления состоянием на основе IndexedDB

В приведенном ниже примере кода реализована простая система управления состоянием в 35 строчек JS кода. Она предлагает следующие функции:

Вы можете определить состояние с помощью имени (строки) и значения (примитив, массив, объект и т. д.). Хранилище объектов IndexedDB сохраняет эти значения, используя имя в качестве индекса.

Любой компонент JavaScript может устанавливать или получать значение по имени.

Когда значение установлено, менеджер состояний предупреждает все подписанные компоненты об изменении. Компонент подписывается через конструктор State или путем установки или получения именованного значения.

Приложение to-do демонстрирует управление состоянием. Оно определяет два веб-компонента, которые обращаются к одному и тому же массиву задач, управляемому объектами State:

todo-list.js: отображает HTML-код списка задач и удаляет элемент, когда пользователь нажимает кнопку «Done».

todo-add.js: показывает форму «add new item», которая добавляет новые задачи в массив todolist.

Примечание. Один компонент todolist был бы более практичным, но проект демонстрирует, как два изолированных класса могут совместно использовать одно и то же состояние.

Создание класса-обёртки IndexedDB

В статье «Начало работы с IndexDB» была представлена оболочка IndexedDB на основе Promise. Нам нужен аналогичный класс, но он может быть проще, потому что он выбирает отдельные записи по имени.

Скрипт js/lib/indexeddb.js определяет класс IndexedDB с конструктором. Он принимает имя базы данных, версию и функцию обновления. Он возвращает созданный объект после успешного подключения к базе данных IndexedDB:

Асинхронный метод set сохраняет значение с идентификатором имени в хранилище объектов storeName. IndexedDB обрабатывает все операции в транзакции, которая запускает события, разрешающие или отклоняющие обещание:

Точно так же асинхронный метод get извлекает значение с идентификатором имени в хранилище объектов storeName:

Создание класса управления состоянием

Скрипт js/lib/state.js импортирует IndexedDB и определяет класс State. Он разделяет пять значений статических свойств для всех экземпляров:

dbName: имя базы данных IndexedDB, используемой для хранения состояний («stateDB»).

dbVersion: номер версии базы данных (1)

storeName: имя хранилища объектов, которое используется для хранения всех пар имя/значение («состояние»).

БД: ссылка на объект IndexedDB, используемый для доступа к базе данных

target: объект EventTarget(), который может отправлять и получать события по всем объектам State.

Конструктор принимает два необязательных параметра:

массив наблюдаемых имен

функцию updateCallback. Эта функция получает имя и значение всякий раз, когда обновляется состояние.

Обработчик прослушивает установленные события, вызываемые при изменении состояния. Он запускает функцию updateCallback, когда переданное имя отслеживается.

Класс не подключается к базе данных IndexedDB, пока это не потребуется. Метод dbConnect устанавливает соединение и повторно использует его для всех объектов State. При первом запуске он создает новое хранилище объектов с именем state (как определено в статическом свойстве storeName):

Асинхронный метод set обновляет именованное значение. Он добавляет имя в наблюдаемый список, подключается к базе данных IndexedDB, устанавливает новое значение и запускает набор CustomEvent, который получают все объекты State:

Асинхронный метод get возвращает именованное значение. Он добавляет имя в наблюдаемый список, подключается к базе данных IndexedDB и извлекает проиндексированные данные:

Вы можете получать и обновлять значения состояния с помощью нового объекта State

Другой код может получать уведомления об обновлении состояния для того же элемента:

Создаём приложение to-do с использованием управления состоянием

Простое приложение со списком дел демонстрирует систему управления состоянием:

В файле index.html определены два настраиваемых элемента:

./js/main.js импортирует оба компонента:

Скрипты создают веб-компоненты без фреймворка, которые получают и устанавливают общее состояние списка задач. Веб-компоненты выходят за рамки этой статьи, но основные моменты:

Вы можете определить собственный HTML-элемент (например, ). Имя должно содержать дефис (-), чтобы избежать конфликтов с текущими или будущими элементами HTML.

Это JavaScript класс, расширяющий HTMLElement, определяет функциональность. Конструктор должен вызывать функцию super().

Браузер вызывает метод connectedCallback(), когда готов обновить DOM. Метод может добавлять контент, при необходимости используя инкапсулированный shadow DOM, недоступный для других скриптов.

customElements.define регистрирует класс с настраиваемым элементом.

компонент

Метод render() получает обновленное имя и значение (поступит только todolist). Он сохраняет список как свойство this объекта, а затем добавляет HTML в Shadow DOM (созданный методом connectedCallback()):

Метод connectedCallback () запускается, когда DOM готов.

Он создает новый Shadow DOM и передает последнее состояние todolist методу render()

Он присоединяет обработчик события клика, который удаляет элемент из состояния списка задач. Метод render() будет выполняться автоматически, поскольку состояние изменилось.

Затем регистрируем класс TodoList для компонента :

компонент

Метод connectedCallback() запускается, когда DOM готов.

Он загружает последнее состояние todolist в локальное свойство, которое по умолчанию представляет собой пустой массив.

Он добавляет HTML форму в Shadow DOM

Он присоединяет обработчик события отправки формы, который добавляет новый элемент в состояние todolist (который, в свою очередь, обновляет компонент ). Затем он очищает поле ввода, чтобы вы могли добавить еще одну задачу.

Затем регистрируем класс TodoAdd для компонента :

Заключение

Проекты часто избегают IndexedDB, потому что его API неуклюжий. Это не очевидный выбор для управления состоянием, но индексированная база данных и большой объем хранилища могут сделать ее хорошим вариантом для сложных проектов, в которых хранятся значительные объемы данных.

Источник

IndexedDB — безлимитное хранение данных

Добрый день, уважаемое сообщество.
Для тех, кто не в курсе, что такое IndexedDB и с чем его едят, можно почитать здесь.

Читайте также:  doppio кофе что это такое

Безлимит

В конторе в которой я работаю появилась необходимость использования индексированной локальной базы данных на стороне клиента и выбор сразу пал на IndexedDB.

Но как всегда есть одно «НО», это самое «НО» — ограничение размера БД на машине пользователя в размере 5 МБ, что отнюдь нас не устраивало. Так как данная технология планировалась использоваться в админке нашего проекта и все юзеры использовали в качестве дефолтного браузера Google Chrome, то было принято решение поиска обхода того самого ограничение через расширение-прокси. Перелопатив много инфы мы пришли к выводу, что ограничение на размер БД можно убрать использовав специальные флаги в манифесте нашего расширения:

Отправка сообщений сайт-расширение-сайт

Идем далее. С безлимитным хранением данных мы разобрались, но теперь возникла необходимость работать с той самой безлимитной БД непосредственно с самого сайта. Для этого использовалась отправка сообщений между сайтом и расширением (расширение выступило в роли прокси, между сайтом и безлимитной БД). Для этого в манифесте нашего расширения добавили следующие флаги:

Выяснилось что валидными считаются URL вида: *://google.com/* и http://*.chromium.org/*, а, http:// * / *, * :/ / *. COM / не являются.
Больше информации о externally_connectable можете почитать здесь.

Наступил этап написания того самого «моста» между сайтом и расширением для доступа к БД.
В качестве основной библиотеки для работы с IndexedDB на стороне расширения была использована db.js, с которой вы можете ознакомиться тут.

Чтобы не изобретать велосипед, было принято решение использовать на стороне сайта синтаксис доступа который реализован в db.js.

Расширение

И так поехали, создаем background.js, который будем прослушивать входящие сообщения, и отвечать на них. Листинг кода привожу ниже:

Но тут нас ждал сюрприз, а именно на выполнении участка кода:

Для разрешения данной проблемы добавим в манифест еще одну строку, которая разрешает выполнение пользовательского js на стороне расширения.

Также пришлось реализовать вспомогательную функцию перегона объекта в массив, для передачи его в качестве аргументов функции.

Полный листинг manifest.json

Клиент

С расширением разобрались, теперь приступим к написанию клиент-библиотеки для работы с нашим прокси-расширением.
Первое, что необходимо, при отправке сообщения с клиента указать какому расширению мы хотим его послать, для этого, указываем его id:

Полный листинг клиентской библиотеки:

На данном этапе наш комплекс для работы с безлимитной indexDB готов. Ниже приведу примеры использования.

Источник

IndexedDB

Для традиционных клиент-серверных приложений эта мощность обычно чрезмерна. IndexedDB предназначена для оффлайн приложений, можно совмещать с ServiceWorkers и другими технологиями.

Интерфейс для IndexedDB, описанный в спецификации https://www.w3.org/TR/IndexedDB, основан на событиях.

Мы также можем использовать async/await с помощью обёртки, которая основана на промисах, например https://github.com/jakearchibald/idb. Это очень удобно, но обёртка не идеальна, она не может полностью заменить события. Поэтому мы начнём с событий, а затем, когда разберёмся в IndexedDB, рассмотрим и обёртку.

Открыть базу данных

Для начала работы с IndexedDB нужно открыть базу данных.

У нас может быть множество баз данных с различными именами, но все они существуют в контексте текущего источника (домен/протокол/порт). Разные сайты не могут получить доступ к базам данных друг друга.

После этого вызова необходимо назначить обработчик событий для объекта openRequest :

IndexedDB имеет встроенный механизм «версионирования схемы», который отсутствует в серверных базах данных.

В отличие от серверных баз данных, IndexedDB работает на стороне клиента, в браузере, и у нас нет прямого доступа к данным. Но когда мы публикуем новую версию нашего приложения, возможно, нам понадобится обновить базу данных.

Это событие также сработает, если базы данных ещё не существует, так что в этом обработчике мы можем выполнить инициализацию.

Например, когда мы впервые публикуем наше приложение, мы открываем базу данных с версией 1 и выполняем инициализацию в обработчике upgradeneeded :

Когда мы публикуем вторую версию:

Удалить базу данных:

Такое может произойти, если посетитель загрузил устаревший код, например, из кеша прокси. Нам следует проверить db.version и предложить ему перезагрузить страницу. А также проверить наши кеширующие заголовки, убедиться, что посетитель никогда не получит устаревший код.

Проблема параллельного обновления

Раз уж мы говорим про версионирование, рассмотрим связанную с этим небольшую проблему.

Допустим, посетитель открыл наш сайт во вкладке браузера, с базой версии 1.

Проблема заключается в том, что база данных всего одна на две вкладки, так как это один и тот же сайт, один источник. И она не может быть одновременно версии 1 и 2. Чтобы обновить на версию 2, все соединения к версии 1 должны быть закрыты.

Код, который это делает:

Здесь мы делаем две вещи:

Хранилище объектов

Чтобы сохранить что-то в IndexedDB, нам нужно хранилище объектов.

Хранилище объектов – это основная концепция IndexedDB. В других базах данных это «таблицы» или «коллекции». Здесь хранятся данные. В базе данных может быть множество хранилищ: одно для пользователей, другое для товаров и так далее.

Несмотря на то, что название – «хранилище объектов», примитивы тоже могут там храниться.

Мы можем хранить почти любое значение, в том числе сложные объекты.

Пример объекта, который нельзя сохранить: объект с циклическими ссылками. Такие объекты не сериализуемы. JSON.stringify также выдаст ошибку при сериализации.

Каждому значению в хранилище должен соответствовать уникальный ключ.

Ключ должен быть одним из следующих типов: number, date, string, binary или array. Это уникальный идентификатор: по ключу мы можем искать/удалять/обновлять значения.

Но для начала нужно создать хранилище.

Синтаксис для создания хранилища объектов:

Обратите внимание, что операция является синхронной, использование await не требуется.

Например, это хранилище объектов использует свойство id как ключ:

Это техническое ограничение. Вне обработчика мы сможем добавлять/удалять/обновлять данные, но хранилища объектов могут быть созданы/удалены/изменены только во время обновления версии базы данных.

Для обновления версии базы есть два основных подхода:

Для простых баз данных второй подход может быть проще и предпочтительнее.

Вот демонстрация второго способа:

Чтобы удалить хранилище объектов:

Транзакции

Термин «транзакция» является общеизвестным, транзакции используются во многих видах баз данных.

Транзакция – это группа операций, которые должны быть или все выполнены, или все не выполнены (всё или ничего).

Например, когда пользователь что-то покупает, нам нужно:

Будет очень плохо, если мы успеем завершить первую операцию, а затем что-то пойдёт не так, например отключат электричество, и мы не сможем завершить вторую операцию. Обе операции должны быть успешно завершены (покупка сделана, отлично!) или необходимо отменить обе операции (в этом случае пользователь сохранит свои деньги и может попытаться купить ещё раз).

Транзакции гарантируют это.

Все операции с данными в IndexedDB могут быть сделаны только внутри транзакций.

Читайте также:  река навля какая рыба имеется

Для начала транзакции:

Несколько readonly транзакций могут одновременно работать с одним и тем же хранилищем объектов, а readwrite транзакций – не могут. Транзакции типа readwrite «блокируют» хранилище для записи. Следующая такая транзакция должна дождаться выполнения предыдущей, перед тем как получит доступ к тому же самому хранилищу.

После того, как транзакция будет создана, мы можем добавить элемент в хранилище, вот так:

Мы сделали четыре шага:

Хранилища объектов поддерживают два метода для добавления значений:

Автоматическая фиксация транзакций

Короткий ответ: этого не требуется.

В следующей 3.0 версии спецификации, вероятно, будет возможность вручную завершить транзакцию, но сейчас, в версии 2.0, такой возможности нет.

Когда все запросы завершены и очередь микрозадач пуста, тогда транзакция завершится автоматически.

Как правило, это означает, что транзакция автоматически завершается, когда выполнились все её запросы и завершился текущий код.

Таким образом, в приведённом выше примере не требуется никакой специальный вызов, чтобы завершить транзакцию.

Такое автозавершение транзакций имеет важный побочный эффект. Мы не можем вставить асинхронную операцию, такую как fetch или setTimeout в середину транзакции. IndexedDB никак не заставит транзакцию «висеть» и ждать их выполнения.

В приведённом ниже коде в запросе request2 в строке с (*) будет ошибка, потому что транзакция уже завершена, больше нельзя выполнить в ней запрос:

Всё потому, что fetch является асинхронной операцией, макрозадачей. Транзакции завершаются раньше, чем браузер приступает к выполнению макрозадач.

Авторы спецификации IndexedDB из соображений производительности считают, что транзакции должны завершаться быстро.

Но ещё лучше выполнять операции вместе, в рамках одной транзакции: отделить транзакции IndexedDB от других асинхронных операций.

Только complete гарантирует, что транзакция сохранена целиком. По отдельности запросы могут выполниться, но при финальной записи что-то может пойти не так (ошибка ввода-вывода, проблема с диском, например).

Чтобы вручную отменить транзакцию, выполните:

Обработка ошибок

Запросы на запись могут выполниться неудачно.

Мы должны быть готовы к этому, не только из-за возможных ошибок на нашей стороне, но и по причинам, которые не связаны с транзакцией. Например, размер хранилища может быть превышен. И мы должны быть готовы обработать такую ситуацию.

При ошибке в запросе соответствующая транзакция отменяется полностью, включая изменения, сделанные другими её запросами.

Делегирование событий

Нужны ли обработчики onerror/onsuccess для каждого запроса? Не всегда. Мы можем использовать делегирование событий.

Все события являются DOM-событиями с фазами перехвата и всплытия, но обычно используется только всплытие.

…А если мы полностью обработали ошибку? В этом случае мы не хотим сообщать об этом.

Поиск по ключам

Есть два основных вида поиска в хранилище объектов:

Методы поиска поддерживают либо точные ключи, либо так называемые «запросы с диапазоном» – IDBKeyRange объекты, которые задают «диапазон ключей».

Диапазоны создаются с помощью следующих вызовов:

Хранилище объектов внутренне сортирует значения по ключам.

Поэтому запросы, которые возвращают много значений, всегда возвращают их в порядке сортировки по ключу.

Поиск по индексированному полю

Для поиска по другим полям объекта нам нужно создать дополнительную структуру данных, называемую «индекс» (index).

Индекс является «расширением» к хранилищу, которое отслеживает данное поле объекта. Для каждого значения этого поля хранится список ключей для объектов, которые имеют это значение. Ниже будет более подробная картина.

Представим, что в нашем books есть 4 книги. Вот картинка, которая показывает, что такое «индекс».

Как уже говорилось, индекс для каждого значения price (второй аргумент) хранит список ключей, имеющих эту цену.

Индексы автоматически обновляются, нам не нужно об этом заботиться.

Сейчас, когда мы хотим найти объект по цене, мы просто применяем те же методы поиска к индексу:

Удаление из хранилища

Метод delete удаляет значения по запросу, формат вызова такой же как в getAll :

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

Курсоры

Такие методы как getAll/getAllKeys возвращают массив ключей/значений.

Но хранилище объектов может быть огромным, больше, чем доступно памяти.

Тогда метод getAll вернёт ошибку при попытке получить все записи в массиве.

Курсоры предоставляют возможности для работы в таких ситуациях.

Объект cursor идёт по хранилищу объектов с заданным запросом (query) и возвращает пары ключ/значение по очереди, а не все сразу. Это позволяет экономить память.

Так как хранилище объектов внутренне отсортировано по ключу, курсор проходит по хранилищу в порядке хранения ключей (по возрастанию по умолчанию).

Основным отличием курсора является то, что request.onsuccess генерируется многократно: один раз для каждого результата.

Вот пример того, как использовать курсор:

Основные методы курсора:

В приведённом выше примере курсор был создан для хранилища объектов.

Но мы также можем создать курсор для индексов. Как мы помним, индексы позволяют искать по полю объекта. Курсоры для индексов работают так же, как для хранилищ объектов – они позволяют экономить память, возвращая одно значение в единицу времени.

Для курсоров по индексам cursor.key является ключом индекса (например price), нам следует использовать свойство cursor.primaryKey как ключ объекта:

Обёртка для промисов

Добавлять к каждому запросу onsuccess/onerror немного громоздко. Мы можем сделать нашу жизнь проще, используя делегирование событий, например, установить обработчики на все транзакции, но использовать async/await намного удобнее.

Давайте далее в главе использовать небольшую обёртку над промисами https://github.com/jakearchibald/idb. Она создаёт глобальный idb объект с промисифицированными IndexedDB методами.

Тогда вместо onsuccess/onerror мы можем писать примерно так:

Обработка ошибок

Мы можем обработать такие ошибки вот так:

Подводный камень: «Inactive transaction»

Как мы уже знаем, транзакции автоматически завершаются, как только браузер завершает работу с текущим кодом и макрозадачу. Поэтому, если мы поместим макрозадачу наподобие fetch в середину транзакции, транзакция не будет ожидать её завершения. Произойдёт автозавершение транзакции. Поэтому при следующем запросе возникнет ошибка.

Для промисифицирующей обёртки и async/await поведение такое же.

Вот пример fetch в середине транзакции:

Следующий inventory.add после fetch (*) не сработает, сгенерируется ошибка «inactive transaction», потому что транзакция уже завершена и закрыта к этому времени.

Решение такое же, как при работе с обычным IndexedDB: либо создать новую транзакцию, либо разделить задачу на части.

Получение встроенных объектов

Это работает в большинстве случаев. Примеры можно увидеть на странице библиотеки https://github.com/jakearchibald/idb.

Итого

IndexedDB можно рассматривать как «localStorage на стероидах». Это простая база данных типа ключ-значение, достаточно мощная для оффлайн приложений, но простая в использовании.

Лучшим руководством является спецификация, текущая версия 2.0, но также поддерживаются несколько методов из 3.0 (не так много отличий) версии.

Использование можно описать в нескольких фразах:

Источник

Сказочный портал