16 липня 2023 р.

LocalStorage, sessionStorage

Об’єкти веб-сховища 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;

Це дозволено з історичних причин і в цілому працює, але зазвичай не рекомендується, оскільки:

  1. Якщо ключ створений користувачем, він може бути будь-яким, наприклад, length або toString, або іншим вбудованим методом localStorage. У цьому випадку getItem/setItem працює нормально, тоді як доступ через об’єкт ні:

    let key = 'length';
    localStorage[key] = 5; // Помилка, не вдається задати length
  2. Існує подія 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).

Завдання

Створіть поле textarea, яке “автозбереже” своє значення при кожній зміні.

Отже, якщо користувач випадково закриє сторінку і знову відкриє її, він знайде свій незавершений текст у полі.

Приклад:

Відкрити пісочницю для завдання.

Навчальна карта