localStorage предоставляет лишь самые базовые функции, это хранилище довольно медленно и не умеет хранить блобы. IndexedDB и WebSQL асинхронны, быстры и поддерживают большие объемы данных, но их API довольно запутан. Кроме того, ни IndexedDB, ни WebSQL не поддерживаются всеми основными браузерами, и, похоже, в ближайшем будущем эта ситуация не изменится.
Если вам нужно писать веб-приложение с оффлайн-режимом, и вы не знаете, с чего начать, то эта статься для вас. Если вы уже пытались работать с локальными хранилищами, и у вас от этого голова пошла кругом — статья и для вас тоже. Мы в Mozilla написали библиотеку localForage, которая значительно облегчает задачу хранения локальных данных в любом браузере.
Почувствовать на своей шкуре все сложности работы с локальным хранилищем мне помогла разработка around — HTML5-клиента для Foursquare. Хотя в этой статье я рассказываю, как использовать localForage, возможно кто-то предпочтёт изучить реальные примеры работы с ней.
localForage — очень простая библиотека JavaScript, которая использует API, похожий на API localStorage, с теми же самыми базовыми методами get, set, remove, clear и length, но имеет ещё несколько важных улучшений:
- асинхронный API с колбэками;
- драйвера IndexedDB, WebSQL и localStorage (самый подходящий драйвер выбирается автоматически в зависимости от возможностей браузера);
- поддержка блобов и произвольных форматов данных, так что можно хранить изображения, файлы и так далее;
- поддержка обещаний ECMAScript 6.
Использование IndexedDB и WebSQL позволяет хранить намного больше данных, чем localStorage. Неблокирующий асинхронный API делает приложение более быстрым и отзывчивым, так как основной поток приложения не подвисает во время выполнения вызовов get/set. Поддержка обещаний позволяет писать чистый код без спагетти из колбэков. Конечно, если вы любите колбэки, можно использовать и их.
Хватит болтовни, покажите, как это работает!
Традиционный API localStorage во многих отношениях очень неплох. Он прост, не навязывает сложные структуры данных и не требует вообще никакого boilerplate-кода. Например, если вам нужно хранить локально конфигурацию приложения, вы можете написать что-то вроде:
// Это информация, которую мы хотим хранить локально var config = { fullName: document.getElementById('name').getAttribute('value'), userId: document.getElementById('id').getAttribute('value') }; // Сохранение конфигурации localStorage.setItem('config', JSON.stringify(config)); // Восстановление при следующей загрузке приложения var config = JSON.parse(localStorage.getItem('config'));
Обратите внимание, что localStorage хранит всю информацию в виде строк, поэтому приходится преобразовывать всё в сериализованный JSON.
Всё очень просто и логично, но сразу можно заметить несколько проблем:
- Cинхронность. Приходится ждать, пока данные будут считаны с диска и обработаны парсером, какого бы размера они ни были. Это ухудшает отзывчивость приложения. Особенно плохо это на мобильных устройствах — блокируется главный потоrу выполнения, пока идёт обращение к данным, из-за чего приложение начинает казаться медленным и подвисающим.
- Всё хранится в виде строк. Нам постоянно приходится использовать
JSON.parseиJSON.stringify. Это потому, что localStorage умеет работать только со строками JavaScript. Никаких чисел, булевых значений, блобов и прочего. Это раздражает при необходимости хранить числа или массивы, а работу с бинарными данными делает практически невозможной (или, по крайней мере чудовищно медленной).
Упрощаем жизнь с помощь localForage
localForage решает обе эти проблемы с помощью API, очень похожего на интерфейс localStorage, но асинхронного. Сравните, насколько он проще эквивалентного кода для IndexedDB.
Код IndexedDB:
// IndexedDB. var db; var dbName = "dataspace"; var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ]; var request = indexedDB.open(dbName, 2); request.onerror = function(event) { // Обработка ошибок. }; request.onupgradeneeded = function(event) { db = event.target.result; var objectStore = db.createObjectStore("users", { keyPath: "id" }); objectStore.createIndex("fullName", "fullName", { unique: false }); objectStore.transaction.oncomplete = function(event) { var userObjectStore = db.transaction("users", "readwrite").objectStore("users"); } }; // После того, как БД создана, добавим туда запись о пользователе var transaction = db.transaction(["users"], "readwrite"); // Как-то отреагируем на окончание процесса записи в базу transaction.oncomplete = function(event) { console.log("All done!"); }; transaction.onerror = function(event) { // Не забываем обрабатывать ошибки }; var objectStore = transaction.objectStore("users"); for (var i in users) { var request = objectStore.add(users[i]); request.onsuccess = function(event) { // Выведем в консоль информацию о каждом добавленном пользователе console.log(event.target.result); }; }
Код localForage:
// Сохраняем информацию о пользователях var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ]; localForage.setItem('users', users, function(result) { console.log(result); });
Код с WebSQL будет несколько короче, чем код с IndexedDB, но всё равно потребует гораздо больше текста, чем с localForage.
Не только строки
Допустим, вы хотите использовать локально или не только текстовые данные о пользователе, но и его аватарку. С localForage это делается очень просто:
// Загружаем фотографию AJAX-запросом var request = new XMLHttpRequest(); // Допустим, нам нужна фотогрфия пользователя с id=1 request.open('GET', "/users/1/profile_picture.jpg", true); request.responseType = 'arraybuffer'; // Когда запрос завершится, сохраним фото локально request.addEventListener('readystatechange', function() { if (request.readyState === 4) { // readyState DONE // Сохраняем данные как есть. С localStorage такое невозможно localForage.setItem('user_1_photo', request.response, function() { // Фото сохранилось, работаем дальше. }); } }); request.send();
Извлечь фотографию из хранилища можно с помощью всего трёх строк кода:
localForage.getItem('user_1_photo', function(photo) { // Создаём data URI или ещё как-нибудь помещаем фото в тег img. console.log(photo); });
Колбэки и обещания
Если вы не любите колбэки, вы можете использовать обещания ES6. Вот так будет выглядеть последний пример, если переписать его с использованием обещаний:
localForage.getItem('user_1_photo').then(function(photo) { // Создаём data URI или ещё как-нибудь помещаем фото в тег img. console.log(photo); });
Конечно, это искусственный и не слишком наглядный пример. Если вы хотите посмотреть на такой стиль программирования в реальном коде — вот подходящий фрагмент из around.
Кроссбраузерность
localForage поддерживает все современные браузеры. IndexedDB доступна во всех современных браузерах кроме Safari ((IE 10+, IE Mobile 10+, Firefox 10+, Firefox for Android 25+, Chrome 23+, Chrome for Android 32+, Opera 15+). Safari и штатный браузер Android (2.1+) используют WebSQL.
В самом крайнем случае, localForage использует localStorage, так что вы по-прежнему можете хранить данные локально, правда без блобов и гораздо медленнее. Но хотя бы преобразование данных в строки JSON происходит в этом случае автоматически.
Библиотека ещё очень молода, часть функционала только планируется, так что присоединяйтесь к разработке, присылайте сообщения об ошибках и патчи, если хотите, чтобы библиотека умела делать больше!
ссылка на оригинал статьи http://habrahabr.ru/company/nordavind/blog/212709/
Добавить комментарий