IndexedDB — хранилище в вашем браузере

IndexedDB — хранилище в вашем браузере

Если вы используете веб-приложения в том же объеме, что и настольные приложения, я уверен, что вы ненавидите тот аспект, что для их работы требуется подключение к сети. Но набор стандартов HTML5 может содержать в себе требуемое решение. Возможность создания отсоединенных от сети приложений разрушает еще одну преграду между веб-приложениями и собственными приложениями. Среди прочего ресурсоемкие веб-приложения стали требовать наличия в браузере эквивалента базы данных, и стандарт IndexedDB обещает удовлетворить это требование. Требуется ли вам обеспечить работу вне сети или просто ускорить загрузку приложения с помощью локального кэширования ресурсов, поддержка работы в автономном режиме представляет собой важный аспект следующего поколения веб-приложений.

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

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

Обзор (других) вариантов

В старые добрые времена для хранения данных о пользователе использовались файлы cookie, но это было в прошлом десятилетии. Файлы cookie слишком малы и недостаточно эффективны. LocalStorage имеет перспективы, но современным высокопроизводительным веб-приложениям для хранения сведений требуется настоящая база данных. Казалось, что подходящим вариантом оказался API WebDatabase, но, как показывает опыт Mozilla, стандартизация SQL может оказаться непростой задачей. Более простой API хранения, построенный на базе индексно-последовательного метода доступа (ISAM), создает основу для IndexedDB API.

Привет, IndexedDB

Проще говоря, IndexedDB — это постоянное хранилище, которое позволяет сохранять и извлекать объекты JavaScript. Эти объекты индексируются на базе свойства ключа "key" или индекса "index" объекта. Кроме того, этот API допускает итерацию по объектам. Таким образом, IndexedDB является не совсем реляционным с точки зрения традиционной базы данных; отношения между различными хранилищами объектов не являются явными. На это хранилище также распространяется действие политики источника.

API IndexedDB доступны в двух версиях:

  • Синхронные API, доступные с веб-модулями (WebWorkers), ожидают завершения операции базы данных и затем возвращают результат этой операции.
  • Асинхронный API, доступный для глобального объекта окна, не ожидает завершения операции базы данных и немедленно возвращает "request". Обработчики событий onsuccess или onerror выполняются по запросу при завершении операций базы данных, при этом состояние операции обычно указан в свойстве "result" события.

В данной публикации рассматривается асинхронный API. 

Поддержка браузеров сегодня

Спецификация IndexedDB все еще является рабочим проектом, но ведущие поставщики браузеров уже выпустили реализации, позволяющие пользователям оценить данный API и предоставить соответствующие отзывы и предложения. Поддержка IndexedDB доступна в:

IndexedDB предоставляется с помощью префиксов поставщиков, таких как moz_indexedDB и webkitIndexedDB в Firefox и Chrome соответственно. В случае с IE вам необходимо зарегистрировать объект ActiveX и инициализировать его. Чтобы создать общую основу для всех браузеров, вам может потребоваться назначить префиксы поставщиков глобальному indexedDB. Это может также потребоваться для используемых отдельными поставщиками констант (например, IDBRange и т. д.).
Более конкретная информация о браузерах доступна по адресу http://caniuse.com/indexeddb.

База данных [IE] [FF] [CH]

После инициализации указанных выше свойств хранилище объектов готово к использованию. Первый этап заключается в открытии этой базы данных с помощью метода "open". Данный код не требует пояснений.

var request = window.indexedDB.open("DatabaseName", "DatabaseDescription");

request.onsuccess = function(event){

var database = event.result;

   write("Database Opened", database);

 };

request.onerror = function(e){

   writeError(e);

};

При открытии базы данных вызывается onsuccess. Если его не существует, то он создается. event.result содержит результаты текущей операции, в данном случае это база данных.

Хранилище объектов [IE] [FF] [CH]

Такие операции, как "get" или "put", в IndexedDB являются транзакционными. Эти операции могут находиться в одной из областей транзакции (только чтение, чтение-запись, чтение моментального снимка или изменение версии), которые следуют простым правилам, чтобы предотвратить наложение операций друг на друга.
Не вдаваясь в теорию о транзакциях, для хранения отдельных объектов можно создать хранилище объектов (логический эквивалент таблицы в реляционном мире).

Обратите внимание на то, что новое хранилище объектов можно создать или удалить только в области транзакции "изменение версии".

var request = db.setVersion(1);
request.onsuccess = function(e){
    write("Version changed to ", db.version, ", so trying to create a database now.");
    var objectStore = db.createObjectStore("BookList", "id", true);
};

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

Существующее хранилище объектов можно открыть с помощью транзакции, созданной в требуемой области. Последняя стока в приведенном ниже примере возвращает хранилище объектов.

var transaction = db.transaction(["BookList"], 0 /*Read-Write*/, 1000 /*Time out in ms*/);
transaction.oncomplete = function(e){write("===== Transaction Complete");};      
var objectStore = transaction.objectStore("BookList");

Помещение на хранение и извлечение [IE] [FF] [CH]

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

var data = {"bookName" : "Name", "price" : 100, "rating":"good"};

var request = objectStore.add(data);

request.onsuccess = function(event){

    document.write("Saved with id ", event.result)

    var key = event.result;

};

Этот ключ можно использовать позднее для получения объекта — даже после перезапуска браузера.

var request = objectStore.get(key);

request.onsuccess = function(event){

    document.write("Got data ", event.result)

};

Кроме сохранения и извлечения, данный API предоставляет метод "delete" для удаления объекта по ключу. Существующие объекты можно изменять с помощью метода "put". Firefox также предоставляет методы getAll и clear для получения всех объектов и очистки хранилища объектов.

Обход [IE] [FF] [CH]

"cursor" позволяет выполнять итерацию по объектам в хранилище. После открытия "cursor" для итерации многократно вызывается метод continue.

keyRange = new IDBKeyRange.bound(1, 10, true, false);
var request = DAO.objectStore.openCursor(keyRange);
request.onsuccess = function(event){
    var cursor = event.result;
    write(cursor.key , cursor.value); 
// cursor.key is the object key, cursor.value is the actual object.
    cursor["continue"]();
};

Диапазон также может быть передан в вызов openCursor для его ограничения и фильтрации этого диапазона. В приведенном выше примере итерация может осуществляться по всем значениям ключа от 1 до 10. 2 последних параметра указывают, следует ли включать нижнюю и верхнюю границы диапазона.

Путь "Index" в IndexedDB [IE] [FF] [CH]

Итерация объектов также может выполняться по свойствам, отличным от ключа. "index" (в качестве дополнительного ключа) требуется создать для необходимых свойств, что можно сделать только в области транзакции "изменение версии".

var index = objectStore.createIndex("priceIndex", "price", true);

Существующие индексы в хранилище объектов также можно получать.

var index = objectStore.index("priceIndex");

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

var keyCursor = index.openKeyCursor(); 
//key=index property, value = primary key

var objectCursor = index.openCursor(); 
//key=index property, value = object

Когда курсор доступен, по нему можно выполнять итерацию, как в приведенном выше примере. Первый из приведенных выше операторов открывает курсор, где значение является основным ключом объекта. Во втором операторе значением является сам объект. В обоих случаях ключ является свойством "price", которое является индексом.

Взгляд в будущее

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

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

Ссылки

Несколько ссылок на дополнительные материалы о IndexedDB: