Перейти к содержимому

localStorage / sessionStorage / cookies / IndexedDB

Браузер предоставляет несколько механизмов хранения с разными областями видимости, лимитами и семантикой; выбор неправильного хранилища — например, JWT в localStorage вместо httpOnly-cookie — классический security-вопрос на senior-собеседованиях.

// localStorage / sessionStorage: синхронный key-value (~5 МБ), только строки
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme'); // 'dark' или null
localStorage.removeItem('theme');
localStorage.clear();
// sessionStorage: то же API, но живёт только в рамках одной вкладки/сессии
// Дублирование вкладки копирует sessionStorage, но копии независимы
sessionStorage.setItem('draft', JSON.stringify({ text: 'hello', ts: Date.now() }));
const draft = JSON.parse(sessionStorage.getItem('draft') ?? 'null');
// storage event: межвкладочная коммуникация (только localStorage, не sessionStorage)
window.addEventListener('storage', e => {
// e.key | e.oldValue | e.newValue | e.storageArea | e.url
if (e.key === 'logout') location.reload();
});
// Оба API синхронны → блокируют main thread при больших данных.
// Для > 1 МБ структурированных данных: IndexedDB.
// Cookies: атрибуты безопасности имеют значение
// Установка через JS (document.cookie — одна пара за раз)
document.cookie = 'pref=dark; Max-Age=2592000; Path=/; SameSite=Lax';
// httpOnly и Secure нельзя выставить из JS — только сервер!
// Правильная установка session-cookie на сервере (Node.js / Express):
// res.cookie('sid', sessionId, {
// httpOnly: true, // недоступен из JS → защита от XSS-кражи
// secure: true, // только HTTPS
// sameSite: 'strict', // защита от CSRF
// maxAge: 3_600_000, // 1 час в мс
// path: '/',
// });
// Чтение всех доступных cookies (httpOnly — не видны)
const cookies = Object.fromEntries(
document.cookie.split('; ').filter(Boolean).map(c => {
const idx = c.indexOf('=');
return [c.slice(0, idx), decodeURIComponent(c.slice(idx + 1))];
})
);
// SameSite=Strict: cookie не отправляется при любом cross-site запросе
// SameSite=Lax : только top-level GET-навигация (Chrome 80+ default)
// SameSite=None : всегда (требует Secure=true)
// IndexedDB: асинхронная объектная БД, гигабайты, structured clone
const request = indexedDB.open('appDB', 1);
request.onupgradeneeded = e => {
const db = e.target.result;
const store = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
store.createIndex('by_date', 'createdAt', { unique: false });
};
request.onsuccess = e => {
const db = e.target.result;
const tx = db.transaction('notes', 'readwrite');
const st = tx.objectStore('notes');
st.add({ text: 'hello', createdAt: Date.now() });
tx.oncomplete = () => console.log('saved');
tx.onerror = () => console.error(tx.error);
};
// Современная обёртка — библиотека idb (Jake Archibald, ~1 КБ gzip):
// import { openDB } from 'idb';
// const db = await openDB('appDB', 1, {
// upgrade(db) { db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true }); }
// });
// const id = await db.add('notes', { text: 'hi', createdAt: Date.now() });
// const all = await db.getAll('notes');
// Cache API (Service Worker): хранение Response-объектов (ресурсы, HTML, JSON)
// Не путать с HTTP-кэшем; управляется вручную через caches.open('v1')

Итог: Правило выбора: чувствительные токены → httpOnly-cookie; UI-настройки → localStorage; per-tab draft → sessionStorage; большие структурированные данные → IndexedDB; офлайн-ресурсы → Cache API.