Об’єкти веб-сховища localStorage
та sessionStorage
дозволяють зберігати дані в браузері у вигляди пар ключ/значення.
Що цікаво, дані зберігаються навіть після оновлення сторінки (для sessionStorage
) і після повного закриття і нового відкриття вікна браузера (для localStorage
). Ми скоро перевіримо це на практиці.
Ми вже маємо cookies
. Навіщо додаткові об’єкти?
- На відміну від файлів cookies, об’єкти веб-сховища не надсилаються на сервер із кожним запитом. Завдяки цьому ми можемо зберігати набагато більше даних. Більшість браузерів дозволяють принаймні 5 мегабайтів даних (або більше), користувач може навіть змінити цей об’єм.
- Крім того, на відміну від файлів cookies, сервер не може маніпулювати об’єктами сховища через HTTP-заголовки. Все зроблено на JavaScript.
- Сховище прив’язане до оригінального сайту (домен/протокол/порт). Таким чином, що різні протоколи або субдомени мають різні об’єкти зберігання, і не можуть отримати доступ до даних один одного.
Обидва об’єкти сховища забезпечують однакові методи та властивості:
setItem(key, value)
– зберегти пару ключ/значення.getItem(key)
– отримати значення за ключем.removeItem(key)
– видалити дані за ключем.clear()
– видалити все.key(index)
– отримати ключ на заданій позиції.length
– кількість збережених елементів.
Як ви можете бачити, це схоже на колекцію Map
(setItem/getItem/removeItem
), але також дозволяє отримати доступ за індексом за допомогою key(index)
.
Давайте подивимося, як це працює.
Демонстрація роботи localStorage
Основними особливостями localStorage
є:
- Спільний доступ з усіх вкладок і вікон для одного і того ж самого сайту.
- Термін дії даних не закінчується. Дані залишаються після перезавантаження браузера і навіть перезавантаження ОС.
Наприклад, якщо ви запустите цей код…
localStorage.setItem('test', 1);
…І закриєте/відкриєте браузер або просто відкриєте ту саму сторінку в іншому вікні, то зможете отримати дані так:
alert( localStorage.getItem('test') ); // 1
Ми повинні бути на тому ж самому сайті (домен/порт/протокол), шлях URL-адреси може відрізнятись
localStorage
доступний для одного сайту в усіх відкритих вікнах, тому якщо ми встановимо дані в одному вікні, зміна стане видимою в іншому.
Доступ як до звичайного об’єкту
Ми також можемо використовувати простий об’єктний спосіб отримання/запису даних, наприклад:
// записати дані за ключем
localStorage.test = 2;
// отримати дані за ключем
alert( localStorage.test ); // 2
// видалити дані
delete localStorage.test;
Це дозволено з історичних причин і в цілому працює, але зазвичай не рекомендується, оскільки:
-
Якщо ключ створений користувачем, він може бути будь-яким, наприклад,
length
абоtoString
, або іншим вбудованим методомlocalStorage
. У цьому випадкуgetItem/setItem
працює нормально, тоді як доступ через об’єкт ні:let key = 'length'; localStorage[key] = 5; // Помилка, не вдається задати length
-
Існує подія
storage
, вона запускається, коли ми змінюємо дані. Ця подія не відбувається при зміні даних без викликуsetItem
. Ми поговоримо про це пізніше в цьому розділі.
Перебір ключів
Як ми бачили, методи забезпечують функцію “отримати/записати/видалити за ключем”. Але як отримати всі збережені значення чи ключі?
На жаль, об’єкти зберігання не є ітераційними.
Один із способів – працювати з ними як з масивом:
for(let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
Інший спосіб – використати цикл for key in localStorage
, так само, як для звичайних об’єктів.
Він перебирає ключі, але також виводить кілька вбудованих властивостей, які нам не потрібні:
// невдала спроба
for(let key in localStorage) {
alert(key); // показує getItem, setItem та інші вбудовані властивості
}
…Тож нам потрібно або відфільтрувати поля з прототипу за допомогою перевірки hasOwnProperty
:
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // пропускати ключі, такі як "setItem", "getItem" тощо
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
…Або просто отримати “власні” ключі за допомогою Object.keys
, а потім перебрати їх, якщо потрібно:
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Останній варіант працює, оскільки Object.keys
повертає лише ключі, які належать об’єкту, ігноруючи прототип.
Тільки рядки
Зверніть увагу, що і ключ, і значення мають бути рядками.
Якщо ми спробуємо використати будь-який інший тип, наприклад число або об’єкт, він автоматично перетвориться на рядок:
localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]
Однак ми можемо використати JSON
для зберігання об’єктів:
localStorage.user = JSON.stringify({name: "Тарас"});
// через деякий час
let user = JSON.parse( localStorage.user );
alert( user.name ); // Тарас
Також можна перетворити весь об’єкт сховища на JSON рядок, наприклад, підчас налагодження коду:
// додано параметри форматування до JSON.stringify, щоб об’єкт виглядав краще
alert( JSON.stringify(localStorage, null, 2) );
sessionStorage
Об’єкт sessionStorage
використовується набагато рідше, ніж localStorage
.
Властивості та методи ті самі, але можливості більш обмежені:
sessionStorage
існує лише на поточній вкладці браузера.- Інша вкладка з тією ж сторінкою матиме інше сховище.
- Але він використовується між iframes на одній вкладці (за умови, що це один сайт).
- Дані зберігаються після оновлення сторінки, але не закриття/відкриття вкладки.
Давайте подивимося на це в дії.
Запустіть цей код…
sessionStorage.setItem('test', 1);
…Потім оновіть сторінку. Тепер ви все ще можете отримати дані:
alert( sessionStorage.getItem('test') ); // після оновлення: 1
…Але якщо ви відкриєте ту саму сторінку в іншій вкладці та спробуєте там ще раз, код вище поверне null
, що означає “нічого не знайдено”.
Це саме тому, що sessionStorage
прив’язаний не лише до сайту, а й до вкладки браузера. З цієї причини sessionStorage
використовується не часто.
Подія storage
Коли дані оновлюються в localStorage
або sessionStorage
, запускається подія storage із властивостями:
key
– ключ, який було змінено (null
, якщо викликається.clear()
).oldValue
– старе значення (null
, якщо це новий ключ).newValue
– нове значення (null
, якщо дані видалено).url
– URL-адреса документа, де відбулося оновлення.storageArea
– об’єктlocalStorage
абоsessionStorage
, де відбулося оновлення.
Важливо: подія запускається на всіх об’єктах window
, де доступне сховище, крім того, що його викликало.
Давайте детальніше.
Уявіть, у вас є два вікна з однаковим сайтом у кожному. Таким чином, localStorage
є спільним між ними.
Ви можете відкрити цю сторінку в двох вікнах браузера, щоб перевірити наведений нижче код.
Якщо обидва вікна прослуховують window.onstorage
, то кожне з них реагуватиме на оновлення, які відбулися в іншому.
// запускає оновлення, створені в тому самому сховищі з інших документів
window.onstorage = event => { // також можна використовувати window.addEventListener('storage', event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " в " + 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
мають бути рядками. - Ліміт становить 5 Мб+, залежить від браузера.
- Дані не мають терміну зберіганя, тобто не видаляються.
- Дані прив’язані до сайту (домен/порт/протокол).
localStorage |
sessionStorage |
---|---|
Спільний для всіх вкладок і вікон з однаковим сайтом | Відображається у вкладці браузера, включаючи iframe того самого сайту |
Не видаляється при перезапуску браузера | Витримує оновлення сторінки (але не закриття вкладки) |
API:
setItem(key, value)
– зберегти пару ключ/значення.getItem(key)
– отримати значення за ключем.removeItem(key)
– видалити дані за ключем.clear()
– видалити все.key(index)
– отримати ключ на заданій позиції.length
– кількість збережених елементів.- Використовуйте
Object.keys
, щоб отримати всі ключі. - Ми маємо доступ до ключів як властивостей об’єкта та можемо ними маніпулювати, у цьому випадку подія
storage
не ініціюється.
Подія storage:
- Спрацьовує на виклики
setItem
,removeItem
,clear
. - Містить усі дані про операцію (
key/oldValue/newValue
), URL-адресу документа та об’єкт сховищаstorageArea
. - Спрацьовує для всіх об’єктів
window
, які мають доступ до сховища, крім того, який його створив (у межах вкладки дляsessionStorage
, глобально дляlocalStorage
).