Web storage api
Содержание:
- Full Example
- Properties
- HTML5-хранилище в действии
- Слежение за областью HTML5-хранилища
- Using JSON for powerful control
- Методы интерфейса
- Configuration
- Setting up JavaScript functionality
- За пределами пары ключ/значение: конкурентное видение
- Хранилище объектов
- Веб-хранилище. Назначение localStorage и sessionStorage
Full Example
import React, { Fragment } from 'react'; import { render } from 'react-dom'; import { writeStorage, deleteFromStorage, useLocalStorage } from '@rehooks/local-storage'; const startingNum = ; const Clicker = () => ( <Fragment> <h4>Clicker<h4> <button onClick={_ => { writeStorage('num', localStorage.getItem('num') ? +(localStorage.getItem('num')) + 1 : startingNum ) }}> Increment From Outside <button> <button onClick={_ => deleteFromStorage('num')}> Delete From Outside <button> <Fragment> ); const IncrememterWithButtons = () => { const number, setNum, deleteNum = useLocalStorage('num'); return ( <Fragment> <p>{typeof(number) === 'number' ? number : 'Try incrementing the number!'}<p> <button onClick={_ => setNum(number !== null ? +(number) + 1 : startingNum)}>Increment<button> <button onClick={deleteNum}>Delete<button> <Fragment> ); }; const App = () => ( <Fragment> <h1> Demo <h1> <IncrememterWithButtons > <Clicker > <Fragment> ); // Assuming there is a div in index.html with an ID of 'root' render(<App >, document.getElementById('root'));
Properties
local
Items in the storage area are local to each machine.
Properties
QUOTA_BYTES
number 5242880
The maximum amount (in bytes) of data that can be stored in local storage, as measured by the JSON stringification of every value plus every key’s length. This value will be ignored if the extension has the unlimitedStorage permission. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError.
managed
Items in the storage area are set by the domain administrator, and are read-only for the extension; trying to modify this namespace results in an error.
Properties
-
MAX_ITEMS
number 512The maximum number of items that can be stored in sync storage. Updates that would cause this limit to be exceeded will fail immediately and set .
-
MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE
number 1000000Deprecated since Chrome 40. The storage.sync API no longer has a sustained write operation quota.
-
MAX_WRITE_OPERATIONS_PER_HOUR
number 1800The maximum number of , , or operations that can be performed each hour. This is 1 every 2 seconds, a lower ceiling than the short term higher writes-per-minute limit.
Updates that would cause this limit to be exceeded fail immediately and set .
-
MAX_WRITE_OPERATIONS_PER_MINUTE
number 120Since Chrome 40.
The maximum number of , , or operations that can be performed each minute. This is 2 per second, providing higher throughput than writes-per-hour over a shorter period of time.
Updates that would cause this limit to be exceeded fail immediately and set .
-
QUOTA_BYTES
number 102400The maximum total amount (in bytes) of data that can be stored in sync storage, as measured by the JSON stringification of every value plus every key’s length. Updates that would cause this limit to be exceeded fail immediately and set .
-
QUOTA_BYTES_PER_ITEM
number 8192The maximum size (in bytes) of each individual item in sync storage, as measured by the JSON stringification of its value plus its key length. Updates containing items larger than this limit will fail immediately and set .
HTML5-хранилище в действии
Давайте посмотрим на HTML5-хранилище в действии. Снова обратимся к игре «уголки», которую мы построили в главе про рисование. С этой игрой связана небольшая проблема: если вы закроете окно браузера посередине игры, то потеряете результаты. Но с HTML5-хранилищем мы можем сохранять процесс игры на месте, в самом браузере. Откройте демонстрацию, сделайте несколько ходов, закройте вкладку браузера, а затем снова ее откройте. Если ваш браузер поддерживает HTML5-хранилище, демонстрационная страница волшебным образом вспомнит точное положение в игре, в том числе, сколько ходов вы сделали, положение каждой фишки на доске и даже выбранную фишку.
Как это работает? Каждый раз, когда происходит изменение в игре, мы будем вызывать эту функцию.
function saveGameState() {
if (!supportsLocalStorage()) { return false; }
localStorage = gGameInProgress;
for (var i = 0; i < kNumPieces; i++) {
localStorage = gPieces.row;
localStorage = gPieces.column;
}
localStorage = gSelectedPieceIndex;
localStorage = gSelectedPieceHasMoved;
localStorage = gMoveCount;
return true;
}
Как видите, используется объект localStorage для сохранения процесса игры (gGameInProgress, логический тип). Далее перебираются все фишки (gPieces, массив JavaScript) и сохраняется строка и столбец для каждой из них. После чего сохраняются некоторые дополнительные состояния игры, включая выбранную фишку (gSelectedPieceIndex, целое число), фишку, которая находится в середине длинной серии прыжков (gSelectedPieceHasMoved, логический тип) и общее число сделанных ходов (gMoveCount, целое число).
При загрузке страницы вместо автоматического вызова функции newGame(), которая бы вернула все переменные в исходные значения, мы вызываем resumeGame(). Функция resumeGame() с помощью HTML5-хранилища проверяет состояние игры в локальном хранилище. Если оно есть, то восстанавливает значения с использованием объекта localStorage.
function resumeGame() {
if (!supportsLocalStorage()) { return false; }
gGameInProgress = (localStorage == «true»);
if (!gGameInProgress) { return false; }
gPieces = new Array(kNumPieces);
for (var i = 0; i < kNumPieces; i++) {
var row = parseInt(localStorage);
var column = parseInt(localStorage);
gPieces = new Cell(row, column);
}
gNumPieces = kNumPieces;
gSelectedPieceIndex = parseInt(localStorage);
gSelectedPieceHasMoved = localStorage == «true»;
gMoveCount = parseInt(localStorage);
drawBoard();
return true;
}
Наиболее важной частью этой функции является оговорка, о которой я упоминал ранее в этой главе и повторю здесь: данные хранятся в виде строк. Если вы храните нечто другое, а не строки, вам нужно конвертировать их при получении
К примеру, флаг о том, что игра в процессе (gGameInProgress) является логическим типом. В функции saveGameState() мы просто храним его и не беспокоимся о типе данных.
localStorage = gGameInProgress;
Но в функции resumeGame() мы должны рассмотреть значение, полученное из локального хранилища в виде строки и вручную построить собственное логическое значение.
gGameInProgress = (localStorage == «true»);
Аналогичным образом, число ходов хранится в gMoveCount как целое, в функции saveGameState() мы просто сохраняем его.
localStorage = gMoveCount;
Но в функции resumeGame() мы должны конвертировать значение в целое, используя встроенную в JavaScript функцию parseInt().
gMoveCount = parseInt(localStorage);
Слежение за областью HTML5-хранилища
Если вы хотите программно отслеживать изменения хранилища, то должны отлавливать событие storage. Это событие возникает в объекте window, когда setItem(), removeItem() или clear() вызываются и что-то изменяют. Например, если вы установили существующее значение или вызвали clear() когда нет ключей, то событие не сработает, потому что область хранения на самом деле не изменилась.
Событие storage поддерживается везде, где работает объект localStorage, включая Internet Explorer 8. IE 8 не поддерживает стандарт W3C addEventListener (хотя он, наконец-то, будет добавлен в IE 9), поэтому, чтобы отловить событие storage нужно проверить, какой механизм событий поддерживает браузер (если вы уже проделывали это раньше с другими событиями, то можете пропустить этот раздел до конца). Перехват события storage работает так же, как и перехват других событий. Если вы предпочитаете использовать jQuery или какую-либо другую библиотеку JavaScript для регистрации обработчиков событий, то можете проделать это и со storage тоже.
if (window.addEventListener) {
window.addEventListener(«storage», handle_storage, false);
} else {
window.attachEvent(«onstorage», handle_storage);
};
Функция обратного вызова handle_storage будет вызвана с объектом StorageEvent, за исключением Internet Explorer, где события хранятся в window.event.
function handle_storage(e) {
if (!e) { e = window.event; }
}
В данном случае переменная e будет объектом StorageEvent, который обладает следующими полезными свойствами.
Свойство | Тип | Описание |
---|---|---|
key | string | Ключ может быть добавлен, удален или изменен. |
oldValue | любой | Предыдущее значение (если переписано) или null, если добавлено новое значение. |
newValue | любой | Новое значение или null, если удалено. |
url* | string | Страница, которая вызывает метод, приведший к изменению. |
* Примечание: свойство url изначально называлось uri и некоторые браузеры поддерживали это свойство перед изменением спецификации. Для обеспечения максимальной совместимости вы должны проверить существует ли свойство url, и если нет проверить вместо него свойство uri.
Событие storage нельзя отменить, внутри функции обратного вызова handle_storage нет возможности остановить изменение. Это просто способ браузеру сказать вам: «Эй, это только что случилось. Вы ничего не можете сделать, я просто хотел, чтобы вы знали».
Using JSON for powerful control
Let’s start with some basic information about localStorage. When storing information in localStorage, you use key/value pairs, like this:
And to read it out, you just get the value of that key:
That’s all good and well, and being able to save at least 5 MB, you have a lot of options. But since localStorage is just string-based, saving a long string with no form of structure isn’t optimal.
Therefore, we can utilize the native JSON support in web browsers to turn JavaScript objects to string for saving, and back into objects when reading:
var cast = { "Adm. Adama" : "Edward James Olmos", "President Roslin" : "Mary McDonnell", "Captain Adama" : "Jamie Bamber", "Gaius Baltar" : "James Callis", "Number Six" : "Tricia Helfer", "Kara Thrace" : " Katee Sackhoff" }; // Stores the JavaScript object as a string localStorage.setItem("cast", JSON.stringify(cast)); // Parses the saved string into a JavaScript object again JSON.parse(localStorage.getItem("cast"));
Методы интерфейса
Метод | Описание | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
---|---|---|---|---|---|---|---|
clear() | При вызове производит очистку всех ключей из объекта хранилища (объекта Storage). | Да | Да | Да | Да | 8.0 | Да |
getItem() | При передаче имени ключа возвращает значение этого ключа, или null, если ключ не существует в данном объекте хранилища (объекте Storage). | Да | Да | Да | Да | 8.0 | Да |
key() | При передаче целого нумерованного значения числа n возвращает имя n-го ключа в хранилище (объекта Storage). | Да | Да | Да | Да | 8.0 | Да |
removeItem() | При передаче имени ключа удаляет этот ключ из объекта хранилища (при наличии). | Да | Да | Да | Да | 8.0 | Да |
setItem() | При передаче имени и значения ключа этот ключ добавляется в хранилище (объект Storage) или обновляется, если он уже существует. | Да | Да | Да | Да | 8.0 | Да |
Configuration
setPrefix
You could set a prefix to avoid overwriting any local storage variables from the rest of your appDefault prefix:
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setPrefix('yourAppName'); });
setStorageType
You could change web storage type to localStorage or sessionStorageDefault storage:
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setStorageType('sessionStorage'); });
setDefaultToCookie
If localStorage is not supported, the library will default to cookies instead. This behavior can be disabled.Default:
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setDefaultToCookie(false); });
setStorageCookie
Set cookie options (usually in case of fallback)expiry: number of days before cookies expire (0 = session cookie). default: path: the web path the cookie represents. default: secure: whether to store cookies as secure. default:
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setStorageCookie(45, '<path>', false); });
setStorageCookieDomain
Set the cookie domain, since this runs inside a the block, only providers and constants can be injected. As a result, service can’t be used here, use a hardcoded string or .No default value
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setStorageCookieDomain('<domain>'); });
For local testing (when you are testing on localhost) set the domain to an empty string ». Setting the domain to ‘localhost’ will not work on all browsers (eg. Chrome) since some browsers only allow you to set domain cookies for registry controlled domains, i.e. something ending in .com or so, but not IPs or intranet hostnames like localhost.
setNotify
Configure whether events should be broadcasted on $rootScope for each of the following actions:setItem , default: , event «LocalStorageModule.notification.setitem»removeItem , default: , event «LocalStorageModule.notification.removeitem»
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setNotify(true, true); });
Configuration Example
Using all together
myApp.config(function (localStorageServiceProvider) { localStorageServiceProvider .setPrefix('myApp') .setStorageType('sessionStorage') .setNotify(true, true) });
Setting up JavaScript functionality
Before we integrate this into local storage, let’s just get the form and list working — we want anything we submit in the to appear in the .
First, I’m just going to set up some variables for the elements on the page — the form, the unordered list, the button, and the text input.
scripts.js
Next, I’m going to make a function that creates an element, since I’ll be doing that more than once. I’ll call the function . It just creates an element, sets the text of the element to the parameter, and appends the list item to the .
scripts.js
I’m going to add an event listener to the form that watches for a submit event — which will be any time you press enter on the form. The will prevent the form from the default submit action, which we don’t want, since we’re not sending any data to a server.
Instead, the form will submit the value of the . We’re going to call the function, which will create the item with the text of the value and append it to the DOM. Finally, we’ll set the value to an empty string so you don’t have to erase the last item entered manually.
scripts.js
Now with paltry few lines of code, we have a little app that adds to-do items to a list.
Since we’re not saving the items anywhere, when you close or refresh the browser, the items will be gone. The final step is to integrate it into local storage so that the data persists.
За пределами пары ключ/значение: конкурентное видение
Хотя в истории было много уловок и обходных путей, нынешнее состояние HTML5-хранилища на удивление благополучно. Новый API был стандартизирован и включен во все основные браузеры, платформы и устройства. Для веб-разработчика такое увидишь не каждый день, не так ли? Но это больше, чем «5 мегабайт пар ключ/значение» и будущее постоянного локального хранилища это… как бы сказать… ну, пусть конкурентное видение.
Одно видение является аббревиатурой, которую вы уже знаете — SQL. В 2007 году Google запустил Gears, кроссбраузерный плагин с открытым исходным кодом, в который включена встроенная база данных на основе SQLite. Этот ранний прототип позже повлиял на создание спецификации Web SQL Database. База данных Web SQL (ранее известная как «WebDB») обеспечивает тонкую оболочку вокруг базы данных SQL, что позволяет делать следующие вещи из JavaScript:
openDatabase(‘documents’, ‘1.0’, ‘Local document storage’, 5*1024*1024, function (db) {
db.changeVersion(», ‘1.0’, function (t) {
t.executeSql(‘CREATE TABLE docids (id, name)’);
}, error);
});
Как вы можете видеть, большая часть действий находится в строке с методом ExecuteSQL. Эта строка может поддерживать любые команды SQL, в том числе SELECT, UPDATE, INSERT и DELETE. Это все равно, что серверное программирования баз данных, за исключением того, что вы делаете это с JavaScript! О радость!
Спецификация базы данных Web SQL была реализована в четырех браузерах и платформах.
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
– | – | 4.0+ | 4.0+ | 10.5+ | 3.0+ | 2.0+ |
Конечно, если вы использовали более чем одну базу данных в своей жизни, то знаете, что «SQL» это скорее маркетинговый термин, чем жесткий и быстрый стандарт (кто-то может сказать то же самое об HTML5, но это не важно). Конечно, есть актуальная спецификация SQL (она называется SQL-92), но в мире нет сервера баз данных, который соответствует только этой спецификации
Есть Oracle SQL, Microsoft SQL, SQL в MySQL, SQL в PostgreSQL, SQL в SQLite. В действительности, каждый из этих продуктов с течением времени добавляет новые функции SQL, так что недостаточно даже произнести «SQL в SQLite». Вы должны сказать «версия SQL, который поставляется вместе с SQLite версии X.Y.Z».
Все это подводит нас к следующей оговорке, в настоящее время размещенной вверху спецификации Web SQL.
Именно на этом фоне я расскажу вам о другом конкурентном видении для продвинутых, постоянное локальное хранилище для веб-приложений: Indexed Database API, ранее известное как «WebSimpleDB», теперь ласково называемое IndexedDB.
Indexed Database API предоставляет то, что называется хранилище объектов, при этом много идей заимствовано из баз данных SQL. Есть «базы данных» с «записями», каждая запись имеет определенное количество «полей». У каждого поля есть определенный тип данных, который определяется при создании базы данных. Вы можете выбрать часть записей, затем перечислить их «курсором». Изменения в хранилище объектов обрабатываются с «транзакциями».
Если вы хоть раз программировали базы данных SQL, то эти термины, вероятно, вам знакомы. Основная разница в том, что хранилище объектов не имеет структурированного языка запросов. Вы не напишите условие вроде «SELECT * from USERS where ACTIVE = ‘Y'». Вместо этого используются методы, предоставляемые хранилищем объектов для открытия базы USERS, перечисления записей, фильтрации наших записей и использование методов доступа для получения значения каждого поля оставшихся записей. An early walk-through of IndexedDB (Ранний проход IndexedDB) это хорошее руководство о том, как работает IndexedDB и сравнение IndexedDB с Web SQL.
На момент написания IndexedDB был реализован только в бета-версии Firefox 4. Для контраста, Mozilla заявила, что никогда не будет воплощать Web SQL. Google заявил, что они рассматривают поддержку IndexedDB для Chromium и Google Chrome. И даже Майкрософт заявил, что IndexedDB «отличное решение для веб».
Что вы как веб-разработчик можете делать с IndexedDB? На данный момент практически ничего, кроме некоторых технологических демонстраций. Через год? Возможно.
Хранилище объектов
Чтобы сохранить что-то в IndexedDB, нам нужно хранилище объектов.
Хранилище объектов – это основная концепция IndexedDB. В других базах данных это «таблицы» или «коллекции». Здесь хранятся данные. В базе данных может быть множество хранилищ: одно для пользователей, другое для товаров и так далее.
Несмотря на то, что название – «хранилище объектов», примитивы тоже могут там храниться.
Мы можем хранить почти любое значение, в том числе сложные объекты.
IndexedDB использует для клонирования и хранения объекта. Это как , но более мощный, способный хранить гораздо больше типов данных.
Пример объекта, который нельзя сохранить: объект с циклическими ссылками. Такие объекты не сериализуемы. также выдаст ошибку при сериализации.
Каждому значению в хранилище должен соответствовать уникальный ключ.
Ключ должен быть одним из следующих типов: number, date, string, binary или array. Это уникальный идентификатор: по ключу мы можем искать/удалять/обновлять значения.
Как мы видим, можно указать ключ при добавлении значения в хранилище, аналогично . Но когда мы храним объекты, IndexedDB позволяет установить свойство объекта в качестве ключа, что гораздо удобнее. Или мы можем автоматически сгенерировать ключи.
Но для начала нужно создать хранилище.
Синтаксис для создания хранилища объектов:
Обратите внимание, что операция является синхронной, использование не требуется
- – это название хранилища, например для книг,
-
– это необязательный объект с одним или двумя свойствами:
- – путь к свойству объекта, которое IndexedDB будет использовать в качестве ключа, например .
- – если , то ключ будет формироваться автоматически для новых объектов, как постоянно увеличивающееся число.
Если при создании хранилища не указать , то нам потребуется явно указать ключ позже, при сохранении объекта.
Например, это хранилище объектов использует свойство как ключ:
Хранилище объектов можно создавать/изменять только при обновлении версии базы данных в обработчике .
Это техническое ограничение. Вне обработчика мы сможем добавлять/удалять/обновлять данные, но хранилища объектов могут быть созданы/удалены/изменены только во время обновления версии базы данных.
Для обновления версии базы есть два основных подхода:
- Мы можем реализовать функции обновления по версиям: с 1 на 2, с 2 на 3 и т.д. Потом в сравнить версии (например, была 2, сейчас 4) и запустить операции обновления для каждой промежуточной версии (2 на 3, затем 3 на 4).
- Или мы можем взять список существующих хранилищ объектов, используя . Этот объект является , в нём есть метод , используя который можно проверить существование хранилища. Посмотреть, какие хранилища есть и создать те, которых нет.
Для простых баз данных второй подход может быть проще и предпочтительнее.
Вот демонстрация второго способа:
Чтобы удалить хранилище объектов:
Веб-хранилище. Назначение localStorage и sessionStorage
Веб-хранилище — это данные, хранящиеся локально в браузере пользователя. Существует 2 типа веб-хранилищ:
- LocalStorage;
- SessionStorage.
В них вы можете хранить информацию в формате ключ-значение. Ключ и значение – это всегда строки.
Если мы попытаемся сохранить в значение элемента хранилища другой тип значений, например, объект, то он будет, перед тем как туда записан, преобразован в строку. В данном случае посредством неявного у него вызова метода . Т.е. в значении элемента этих хранилищ кроме строкового типа данных никакого другого содержаться не может.
Отличие между этими хранилищами сводится только к периоду времени, в течение которого они могут хранить данные, помещенные в них:
- SessionStorage – выполняет это в течение определённого промежутка времени (сессии). Закрытие вкладки или браузера приводит их к удалению. При этом данные в SessionStorage сохраняются при обновлении страницы.
- LocalStorage – осуществляет это в течение неограниченного времени. Они сохраняются при перезагрузке браузера и компьютера. Их длительность хранения ничем не ограничена. Но, хоть эти данные могут храниться бесконечно в браузере, обычный пользователь может их очень просто удалить, например выполнив очистку истории (при включенной опции «файлы cookie и другие данные сайтов»).
Хранилище LocalStorage похоже на cookies. Оно также применяется для хранения данных на компьютере пользователя (в браузере). Но кроме общих сходств имеется также и много отличий.
Cookies vs. LocalStorage: В чём разница
Отличия между cookies и LocalStorage:
- по месту хранения (куки и данные LocalStorage хранятся на компьютере пользователя в браузере);
- по размеру (cookies ограничены 4 Кбайт, а размер LocalStorage — 5 Мбайт);
- по включению этих данных в HTTP-заголовок (куки в отличие от данных локального хранилища включаются в состав запроса при отправке его на сервер, а также сервер их может добавлять в ответ при отправке его клиенту; таким образом cookies являются частью HTTP-протокола, и увеличивают объём передаваемых данных от клиента серверу и обратно);
- по доступности данных (печеньки можно прочитать и установить как на сервере, так и на клиенте; на клиенте доступны все куки, кроме тех, у которых установлен флаг ; LocalStorage доступны только в браузере посредством JavaScript API);
- по времени хранения данных (куки хранятся ограниченное время (до конца сеанса или истечения указанной даты), нахождение данных в локальном хранилище не ограничено по времени);
- по удобству использования в JavaScript (работа с LocalStorage в JavaScript организовано намного удобнее чем с cookies);
- по необходимости информирования пользователей Евросоюза (при использовании cookies сайт в ЕС должен получать на это разрешение от пользователей; для данных локального хранилища это не требуется);
- по назначению (куки в основном используются для управления сеансом, персонализации и отслеживания действий пользователя, в то время как LocalStorage применяется в качестве обычного локального хранилища информации на компьютере пользователя).
Что использовать: LocalStorage или cookies? На самом деле, ответ на этот вопрос очень прост. Если вам не нужно отправлять данные с каждым HTTP-запросом на сервер, то в этом случае лучше использовать для хранения данных LocalStorage.
Безопасность данных
Хранилище LocalStorage привязана к источнику (домену, протоколу и порту). Данные, находящиеся в некотором источнике, доступны для всех сценариев страниц этого же источника. Из сценария, находящегося в одном источнике, нельзя получить доступ к данным, определяемым другим источником.
Хранилище SessionStorage ограничена только одной вкладкой браузера. Это означает, что в сценарии, находящемся в одной вкладке, нельзя получить информацию из сессионного хранилища другой вкладки даже у них одинаковые источники.
Итоги
Основные характеристики LocalStorage и SessionStorage:
- данные хранятся в виде пар «ключ-значение»;
- хранить можно только строки;
- если вам необходимо хранить в этих хранилищах массивы и объекты, то сначала вы должны их превратить в строки, например, используя метод . Для преобразования строки обратно в массив или объект, можно использовать . Подробнее об этом позже.