IndexedDB: учебник по хранению данных в браузере

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

Для чего используется IndexedDB?

Логично, что не только серверы должны хранить данные клиентов, но и сами клиенты могут хранить определенную информацию о сайте. Потому что это ускоряет работу браузера, так как не нужно заново загружать все данные при каждом посещении. Кроме того, это позволяет веб-приложениям быть доступными и в автономном режиме. Пользовательские записи также могут храниться в удобном для клиента месте. Для последнего, собственно, и предназначены файлы cookie. Но они имеют лишь очень ограниченный объем полезного пространства для хранения — слишком ограниченный для современных веб-приложений. Более того, файлы cookie приходится отправлять по сети при каждом HTTP-запросе.

Одно из решений предлагает веб-хранилище — также известное как хранилище DOM. Этот метод по-прежнему основан на концепции cookie, но его объем увеличивается с нескольких килобайт до 10 МБ. Но даже это не так уж и много. Более того, эти файлы, часто называемые суперкуки, имеют очень базовую структуру. Вы не найдете здесь свойств современной базы данных. Однако cookies и super cookies являются неоптимальным решением не только из-за своего малого размера; оба формата не допускают структурированных данных или индексов, что делает поиск невозможным.

Разработка Web SQL изначально обещала фундаментальные изменения: хранение данных на стороне клиента на основе SQL. Но World Wide Web Consortium (W3C) — организация по разработке веб-стандартов — остановила работу в пользу IndexedDB. Благодаря инициативе Mozilla появился стандарт, который теперь поддерживается большинством современных браузеров.

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

Chrome Firefox Opera Opera mini Safari IE Edge

Что позволяет IndexedDB?

В первую очередь, стандарт представляет собой интерфейс, установленный в браузере. Веб-сайты могут использовать его для хранения информации непосредственно в браузере. Это работает через JavaScript. Таким образом, каждый сайт может создать свою собственную базу данных. И только соответствующий сайт может получить доступ к IndexedDB (сокращение от Indexed Database API). Это означает, что данные остаются конфиденциальными. В базах данных доступны несколько типов хранения объектов. Они позволяют хранить различные форматы: строки, числа, объекты, массивы и записи данных.

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

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

Безопасность играет важную роль, когда речь идет об IndexedDB. Необходимо обеспечить, чтобы веб-сайты не могли получить доступ к базам данных других веб-сайтов. Для этого IndexedDB установил политику same-origin, в которой домен, протокол прикладного уровня и порт должны быть одинаковыми, иначе данные не будут предоставлены. Тем не менее, подпапка одного домена может получить доступ к IndexedDB другой подпапки, так как обе имеют одинаковое происхождение. Однако доступ невозможен, если используется другой порт или протокол переключен с HTTP на HTTPS, или наоборот.

Учебник по IndexedDB: Применение технологии

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

Примечание

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

if (!window.indexedDB) {
	alert("IndexedDB is not supported!");
}

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

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

var request = window.indexedDB.open("MyDatabase", 1);

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

  • ошибка: В процессе генерации произошла ошибка.
  • upgradeeneeded: Версия базы данных изменилась. Подобное событие возникает и при создании базы данных, так как при этом также изменяется номер версии: с несуществующей на единицу.
  • success: База данных может быть успешно открыта.

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

request.onupgradeneeded = function(event) {
	var db = event.target.result;
	var objectStore = db.createObjectStore("User", { keyPath: "id", autoIncrement: true });
}

Наше хранилище объектов содержит имя User. Ключом является id, простая система нумерации, которую мы можем настроить на постоянное увеличение с помощью autoIncrement. Теперь вы можете вставить данные в базу данных или хранилище объектов. Для этого сначала создайте один или несколько индексов. В нашем примере мы хотим создать индекс для имени пользователя и индекс для используемых адресов электронной почты.

objectStore.createIndex("Nickname", "Nickname", { unique: false });
objectStore.createIndex("eMail", "eMail", { unique: true });

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

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

  • только для чтения: Считывает данные из хранилища объектов. Несколько транзакций этого типа могут выполняться одновременно, даже если они относятся к одной и той же области.
  • чтение-запись: Считывание и создание записей. Эти транзакции могут выполняться одновременно, только если они относятся к разным областям.
  • versionchange (изменение версии): Выполняет изменения в хранилище объектов или индексе, но также может создавать и изменять записи. Этот режим не может быть создан вручную, а запускается автоматически событием upgradeeneeded.

Чтобы создать новую запись, используйте readwrite.

const dbconnect = window.indexedDB.open('MyDatabase', 1);
dbconnect.onupgradeneeded = ev => {
  console.log('Upgrade DB');
  const db = ev.target.result;
  const store = db.createObjectStore('User', { keyPath: 'id', autoIncrement: true });
  store.createIndex('Nickname', 'Nickname', { unique: false });
  store.createIndex('eMail', 'eMail', { unique: true });
}
dbconnect.onsuccess = ev => {
  console.log('DB-Upgrade needed');
  const db = ev.target.result;
  const transaction = db.transaction('User', 'readwrite');
  const store = transaction.objectStore('User');
  const data = [
    {Nickname: 'Raptor123', eMail: 'raptor@example.com'},
    {Nickname: 'Dino2', eMail: 'dino@example.com'}
  ];
  data.forEach(el => store.add(el));
  transaction.onerror = ev => {
    console.error('An error has occured!', ev.target.error.message);
  };
  transaction.oncomplete = ev => {
    console.log('Data has been added successfully!');
    const store = db.transaction('User', 'readonly').objectStore('User');
    //const query = store.get(1); // single query
    const query = store.openCursor()
    query.onerror = ev => {
      console.error('Request failed!', ev.target.error.message);
    };
    /*
    // Processing of single query 
    query.onsuccess = ev => {
      if (query.result) {
        console.log('Dataset 1', query.result.Nickname, query.result.eMail);
      } else {
        console.warn('No entry available!');
      }
    };
    */
    query.onsuccess = ev => {
      const cursor = ev.target.result;
      if (cursor) {
        console.log(cursor.key, cursor.value.Nickname, cursor.value.eMail);
        cursor.continue();
      } else {
        console.log('No more entries!');
      }
    };
  };
}

Это позволяет вам вставлять информацию в хранилище объектов. Более того, вы можете использовать это для отображения отчетов через консоль в зависимости от того, была ли транзакция успешной. Любые данные, которые вы добавили в IndexedDB, вы также обычно хотите иметь возможность считывать. Для этого вы можете использовать get.

var transaction = db.transaction(["User"]);
var objectStore = transaction.objectStore("User");
var request = objectStore.get(1);

request.onerror = function(event) {
  console.log("Request failed!");
}

request.onsuccess = function(event) {
  if (request.result) {
    console.log(request.result.Nickname);
    console.log(request.result.eMail);
  } else {
    console.log("No more entries!");
  }
};

Этот код позволяет искать запись по ключу 1 — т.е. по значению id 1. Если транзакция не удалась, появляется сообщение об ошибке. Но если транзакция пройдет, вы узнаете содержание обеих записей — ник и email. Вам также сообщат, если для номера не найдено ни одной записи.

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

var objectStore = db.transaction("User").objectStore("User");
objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    console.log(cursor.key);
    console.log(cursor.value.Nickname);
    console.log(cursor.value.eMail);
    cursor.continue();
  } else {
    console.log("No more entries!");
  }
};

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

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

index.get("Raptor123").onsuccess = function(event) {
  console.log(event.target.result.eMail);
};

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

 

var request = db.transaction(["User"], "readwrite")
  .objectStore("User")
  .delete(1);

request.onsuccess = function(event) {
  console.log("Entry successfully deleted!");
};
Резюме

Эта статья познакомила вас с первыми шагами работы с IndexedDB. Более подробную информацию вы можете найти, например, в Mozilla или Google. В коде примера, однако, Google использует специальную библиотеку, поэтому код немного отличается от кода Mozilla.

Оцените статью
cdelat.ru
Добавить комментарий