Вы когда-нибудь работали с куки? Казалось ли вам при этом, что их использование организовано просто и понятно? Полагаю, что в работе с куки есть множество нюансов, о которых стоит знать новичкам.
Свойство document.cookie
Взглянем на классический способ работы с куки. Соответствующая спецификация существует, благодаря Netscape, с 1994 года. Компания Netscape реализовала свойство document.cookie
в Netscape Navigator в 1996 году. Вот определение куки из тех времён:
Куки — это небольшой фрагмент информации, хранящийся на клиентской машине в файле cookies.txt.
Главу про document.cookie
даже можно найти во втором издании книги «Javascript. The Definitive Guide», которое вышло в январе 1997 года. Это было 24 года тому назад. И мы всё ещё пользуемся тем же старым способом работы с куки ради обратной совместимости.
Как же это выглядит?
Получение куки
const cookies = document.cookie;
// возвращает "_octo=GH1.1.123.456; tz=Europe%2FMinsk" on GitHub
Да, именно так всё и делается. В нашем распоряжении оказывается строка со всеми значениями, хранящимися в куки-файле, разделёнными точкой с запятой.
Как вытащить из этой строки отдельное значение? Если вам кажется, что для этого надо самостоятельно разбить строку на части — знайте, что так оно и есть:
function getCookieValue(name) {
const cookies = document.cookie.split(';');
const res = cookies.find(c => c.startsWith(name + '='));
if (res) {
return res.substring(res.indexOf('=') + 1);
}
}
Как узнать о том, когда истекает срок действия какого-нибудь из куки? Да, в общем-то, никак.
А как узнать домен, с которого установлен какой-нибудь куки? Тоже никак.
Правда, если надо, можно распарсить HTTP-заголовок Cookie
.
Установка куки
document.cookie = 'theme=dark';
Вышеприведённая команда позволяет создать куки с именем theme
, значением которого является dark
. Хорошо. Значит ли это, что document.cookie
— это строка. Нет, не значит. Это — сеттер.
document.cookie = 'mozilla=netscape';
Эта команда не перезапишет куки с именем theme
. Она создаст новый куки с именем mozilla
. Теперь у нас имеются два куки.
По умолчанию срок действия куки, созданного так, как показано выше, истекает после закрытия браузера. Но при создании куки можно указать срок его действия:
document.cookie = 'browser=ie11; expires=Tue, 17 Aug 2021 00:00:00 GMT';
Да. Это — как раз то, что мне нужно — подбирать дату и время истечения срока действия куки в формате GMT каждый раз, когда нужно установить куки. Ладно, давайте воспользуемся для решения этой задачи JavaScript-кодом:
const date = new Date();
date.setTime(date.getTime() + (30 * 24 * 60 * 60 * 1000)); // здорово-то как
document.cookie = `login=mefody; expires=${date.toUTCString()}; path=/`;
Но, к счастью, у нас есть и другой способ установки момента истечения срока действия куки:
document.cookie = 'element=caesium; max-age=952001689';
Свойство max-age
представляет собой срок «жизни» куки в секундах. Соответствующее значение имеет более высокий приоритет, чем то, которое задано с помощью свойства expires
.
И не надо забывать о свойствах path
и domain
. По умолчанию куки устанавливаются для текущего расположения и текущего хоста. Если надо установить куки для всего домена — надо будет добавить к команде установки куки конструкцию такого вида:
; path=/; domain=example.com
Удаление куки
document.cookie = 'login=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
Для удаления куки надо установить дату и время истечения срока действия куки на какой-нибудь момент из прошлого. Для того чтобы всё точно сработало бы — тут рекомендуется использовать начало эпохи Unix.
Работа с куки в сервис-воркерах
Это просто невозможно. Дело в том, что работа с document.cookie
— это синхронная операция, в результате воспользоваться ей в сервис-воркере нельзя.
Cookie Store API
Существует черновик стандарта одного замечательно API, направленного на работу с куки, который способен значительно облегчить нам жизнь в будущем.
Во-первых — это асинхронный API, а значит — пользоваться им можно, не блокируя главный поток. Применять его можно и в сервис-воркерах.
Во-вторых — этот API устроен гораздо понятнее, чем существующий механизм работы с куки.
▍Получение куки
const cookies = await cookieStore.getAll();
const sessionCookies = await cookieStore.getAll({
name: 'session_',
matchType: 'starts-with',
});
Метод getAll
возвращает массив, а не строку. Именно этого я и жду, когда пытаюсь получить некий список.
const ga = await cookieStore.get('_ga');
/**
{
"domain": "mozilla.org",
"expires": 1682945254000,
"name": "_ga",
"path": "/",
"sameSite": "lax",
"secure": false,
"value": "GA1.2.891784426.1616320570"
}
*/
А вот — приятная неожиданность. Можно узнать и дату истечения срока действия куки, и сведения о домене и пути, и при этом не пользоваться никакими хаками.
▍Установка куки
await cookieStore.set('name', 'value');
Или так:
await cookieStore.set({
name: 'name',
value: 'value',
expires: Date.now() + 86400,
domain: self.location.host,
path: '/',
secure: self.location.protocol === 'https:',
httpOnly: false,
});
Мне очень нравится этот синтаксис!
▍Удаление куки
await cookieStore.delete('ie6');
Или можно, как раньше, установить дату истечения срока действия куки на некий момент в прошлом, но не вижу причины поступать именно так.
▍События куки
cookieStore.addEventListener('change', (event) => {
for (const cookie in event.changed) {
console.log(`Cookie ${cookie.name} changed to ${cookie.value}`);
}
});
Как видите, теперь у нас есть возможность подписываться на изменения куки, не занимаясь опросом document.cookie
, блокирующим главный поток. Фантастика!
▍Сервис-воркеры
// service-worker.js
await self.registration.cookies.subscribe([
{
name: 'cookie-name',
url: '/path-to-track',
}
]);
self.addEventListener('cookiechange', (event) => {
// обработка изменений
});
Можно ли пользоваться этим API прямо сейчас?
Хотя этим API уже можно пользоваться, но тут надо проявлять осторожность. Cookie Store API работоспособно в Chrome 87+ (Edge 87+, Opera 73+). В других браузерах можно воспользоваться полифиллом, который, правда, не возвращает полной информации о куки, как это сделано в настоящем API. Прогрессивные улучшения — это вещь.
И учитывайте, что спецификация этого API всё ещё находится в статусе «Draft Community Group Report». Но если в вашем проекте важен хороший «опыт разработчика» — попробуйте современный способ работы с куки.
Пробовали ли вы работать с куки по-новому?
Автор: ru_vds