LocalStorage vs sessionStorage
Что интересного в них, так это то, что данные сохраняются даже после обновления страницы (для sessionStorage
) и полного перезапуска браузера (для localStorage
).
У нас уже есть куки. Почему может возникнуть необходимость в дополнительных средствах хранения?
- Данные сохраняются только локально и не могут быть прочитаны сервером, что устраняет проблему безопасности, которую представляют cookie-файлы.
- Это позволяет сохранять гораздо больше данных (10 МБ для большинства браузеров).
- Хранилище привязано к источнику (домен / протокол / триплет порта). То есть разные протоколы или субдомены определяют разные объекты хранения, они не могут получить доступ к данным друг от друга.
Оба объекта хранения предоставляют одинаковые методы и свойства:
setItem(key, value)
- сохранить элемент как пару ключ / значение.getItem(key)
- получить значение по ключу.removeItem(key)
- удалить элемент.clear()
- очистить или удалить все.key(index)
- получить ключ на основе его индекса.length
- возвращает общее количество сохраненных элементов.
Как вы заметили, это похоже на коллекцию Map
(setItem/getItem/removeItem
), которая также поддерживает порядок элементов и позволяет осуществлять доступ по индексу с помощью key(index)
.
LocalStorage
Основные характеристики:
- Данные распределяются между всеми вкладками и окнами из одного источника.
- Срок действия данных не истечет. Он останется даже после перезагрузки браузера и выживет после перезагрузки ОС.
Например, если вы выполните этот код:
localStorage.setItem('localStorage', 1);
И закройте / откройте браузер или просто откройте ту же страницу в другом окне, результатом выполнения этого кода будет:
alert( localStorage.getItem('localStorage') ); // 1
Нам нужно только быть в одном источнике (домен / порт / протокол), путь URL может быть другим.
Так как он используется всеми окнами с одинаковым источником, следовательно, если мы установим данные в одном окне, изменение будет видно и в другом окне.
Работать только со строками
Оба «key» и «value» должны быть строками. Если мы укажем любой другой тип, например число или объект, он автоматически преобразуется в строку:
sessionStorage.user = {name: "Batman"};
alert(sessionStorage.user); // [object Object]
Мы можем использовать JSON
для хранения объектов:
sessionStorage.user = JSON.stringify({name: "Batman"});// sometime later
let user = JSON.parse( sessionStorage.user );
alert( user.name ); // Batman
Мы также можем структурировать весь объект хранения, например, для целей отладки:
// used JSON.stringify to make the object look nicer
alert( JSON.stringify(localStorage, null, 2) );
Доступ к нему как к объекту
Мы также можем использовать простой способ получения / установки ключей, например:
// set key
localStorage.object = 2;// get key
alert( localStorage.object); // 2
// remove key
delete localStorage.object;
Это разрешено по историческим причинам и в основном работает. Однако это не рекомендуется по следующим причинам:
1. Если ключ генерируется пользователем, это может быть что угодно, например, length
или toString
, или другой встроенный метод localStorage. В этом случае getItem/setItem
работает нормально, в то время как объектно-подобный доступ не удается
let key = 'length';
localStorage[key] = 5; // Error, can't assign length
2. Есть событие storage
, которое срабатывает, когда мы изменяем данные. Это событие не происходит для объектного доступа.
Перебор всех ключей в цикле
Как мы уже видели, методы обеспечивают функциональность «get/set/remove
». Но как мы можем получить все сохраненные значения или ключи?
Мы не можем перебирать объекты хранения. Мы можем перебрать их как массив:
for(let i=0; i
Или мы можем использовать цикл for key in localStorage
, как обычные объекты.
Он перебирает ключи, но также выводит несколько встроенных полей, которые нам не нужны:
// Not gonna work as intended
for(let key in localStorage) {
alert(key); // shows getItem, setItem and other built-in stuff
}
Поэтому нам нужно отфильтровать поля из прототипа с помощью проверки hasOwnProperty
:
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // skip keys like "setItem", "getItem" etc
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
Или просто возьмите «свои» ключи с помощью Object.keys
и затем переберите их при необходимости:
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Последний работает, потому что Object.keys
возвращает только ключи, принадлежащие объекту, игнорируя прототип.
sessionStorage
Использование объекта sessionStorage
значительно меньше чем localStorage
.
Свойства и методы одинаковы, однако их функциональность гораздо более ограничена:
sessionStorage
существует только в текущей вкладке браузера. Другая вкладка с той же страницей будет иметь другое хранилище.- Однако он разделен с
iframes
на одной вкладке (при условии, что они происходят из одного источника) - Данные сохраняются после обновления страницы, но не когда закрывается / открывается вкладка.
например:
sessionStorage.setItem('sessionStorage', 1);
После обновления страницы вы все равно можете получить данные:
alert( sessionStorage.getItem('sessionStorage') );
// after refresh: 1
Однако, если вы откроете ту же страницу в другой вкладке и повторите попытку там, приведенный выше код вернется null
, что означает «ничего не найдено».
Именно потому, что sessionStorage
привязан не только к источнику, но и к вкладке браузера. По этой причине он используется меньше.
Событие хранения
Триггеры событий хранения (со свойствами), срабатывают когда происходит изменение в данных localStorage
или sessionStorage
:
key
- ключ, который был изменен.oldValue
- старое значение (null
если ключ добавлен впервые).newValue
- новое значение (null
если ключ удален).url
- URL документа, где произошло обновление.storageArea
-localStorage
илиsessionStorage
, где произошло обновление.
Важно отметить: событие срабатывает на всех объектах window
, где доступно хранилище, кроме того, который его вызвал.
Давайте посмотрим на это более подробно.
Предположим, у нас есть два окна с одним сайтом в каждом. Наш localStorage
делится между ними. Вы можете открыть эту страницу в двух окнах браузера, чтобы проверить приведенный ниже код.
Если оба окна прослушивают window.onstorage
, то каждое из них будет реагировать на обновления, произошедшие в другом.
// triggers on updates made to the same storage from other documents
window.onstorage = event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " at " + event.url);
};
localStorage.setItem('now', Date.now());
Обратите внимание, что событие также содержит: event.url
- URL-адрес документа, в котором были обновлены данные.
Кроме того, event.storageArea
содержит объект хранения - событие одинаково для как sessionStorage
так и для localStorage
поэтому event.storageArea
ссылается на тот, который был изменен. Мы можем даже захотеть что-то вернуть, «ответить» на изменения.
Это позволяет различным окнам из одного источника обмениваться сообщениями.
Современные браузеры также поддерживают Broadcast channel API, специальный API для межоконной связи того же происхождения, он более функциональный, но менее поддерживается. Существуют библиотеки, которые заполняют этот API, основываясь на localStorage
, что делают его доступным везде.
Резюме
Веб-хранилище объектов localStorage
и sessionStorage
позволяет нам хранить ключ / значение в браузере.
key
иvalue
должны быть строки.- Предел 2mb +, это зависит от браузера.
- Они не имеют ограничения по времени хранения.
- Данные привязаны к источнику (домен / порт / протокол).