JavaScript - sessionStorage и localStorage

Урок, в котором рассматриваются объекты API HTML5 sessionStorage и localStorage, предназначенные для сохранения данных и управления ими на устройствах пользователей.

Авторам при реализации некоторого функционала на веб-сайте иногда приходится сохранять данные на устройстве пользователя для того, чтобы потом к ним можно было обратиться. Реализовать эту возможность позволяют объекты API HTML5 sessionStorage и localStorage.

Объекты sessionStorage и localStorage

Отличаются эти объекты друг от друга только тем, что имеют различный период времени хранения данных, помещённых в них. Объект sessionStorage хранит данные ограниченное время, они удаляются сразу после того как пользователь завершает свой сеанс или закрывает браузер. Объект localStorage в отличие от объекта sessionStorage хранит данные неограниченное время.

Контейнеры localStorage и sessionStorage хранят данные с помощью элементов (пар "ключ-значение"). Ключ представляет собой некоторый идентификатор, который связан со значением. Т.е. для того чтобы записать или получить некоторое значение необходимо знать его ключ. Значение представляет собой строку, это необходимо учитывать при работе с ним в коде JavaScript. Если Вам необходимо вместо строки записать в хранилище сложный объект, то одним из вариантов решения этой задачи может стать его сериализация в JSON с помощью функции JSON.stringify().

Устройство объектов sessionStorage и localStorage

Проверить, поддерживает ли браузер эти API можно с помощью следующей строки:

if (window.sessionStorage && window.localStorage) {
  //объекты sessionStorage и localtorage поддерживаются
}
else {
  //объекты sessionStorage и localtorage не поддерживаются
}

Проверить, поддерживает ли браузер эти API можно с помощью следующей строки:

.getItem(key)
Метод getItem(key) используется для получения значения элемента хранилища по его ключу (key).
.setItem(key,value)
Метод setItem(key,value) предназначен для добавления в хранилище элемента с указанным ключом (key) и значением (value). Если в хранилище уже есть элемент с указанным ключом (key), то в этом случае произойдет изменения его значения (value).
.key(индекс)
Метод key(индекс) возвращает ключ элемента по его порядковому номеру (индексу), который находится в данном хранилище.
.removeItem(key)
Метод removeItem(key) удаляет из контейнера sessionStorage или localStorage элемент, имеющий указанный ключ.
.clear()
Метод clear() удаляет все элементы из контейнера.
.length
Свойство length возвращает количество элементов, находящихся в контейнере.

Работу с хранилищем localStorage рассмотрим на следующих примерах:

1. Добавить значение, содержащее цвет фона в хранилище. Доступ к данному значению будем осуществлять по ключу bgcolor.

localStorage.setItem("bgColor","green");

2. Получить цвет фона из хранилища по ключу bgColor. Установить этот цвет фона странице.

var bgColor = localStorage.getItem("bgColor");
$("body").css("background-color",bgColor);

3. Узнать какое имя имеет ключ, который хранится в 1 элементе массива localStorage:

//получить ключ 1 элемента (0 индекс) массива localStorage:
var key=localStorage.key(0);

4. Удалить из контейнера localStorage элемент, имеющий ключ bgcolor:

localStorage.removeItem("bgColor");

5. Удалить из контейнера localStorage все элементы:

localStorage.clear();

6. Перебрать все элементы, находящиеся в контейнере localStorage.

<div id="elements"></div>
<script>
var str="";
for (var i=0; i < localStorage.length; i++) {
  str += "Ключ: " + localStorage.key(i) + "; Значение: " + localStorage.getItem(localStorage.key(i)) + ".<br>";
}
document.getElementById("elements").innerHTML = str;
</script>

7. Сохранить в элемент контейнера localStorage сложный объект

var data = {
  data1: "Значение",
  data2: "Значение",
  data3: "Значение"
  //...
}
localStorage.setItem("Ключ", JSON.stringify(data));

8. Получить значение сложного объекта из элемента контейнера localStorage.

var data = {};
if (localStorage.getItem("Ключ")) {
  data = JSON.parse(localStorage.getItem("Ключ"));
}

Работа с хранилищем sessionStorage осуществляется аналогичным способом.

В большинстве браузерах размер контейнера localStorage составляет 5 Мбайт. Эта цифра является большой и достаточной для того, чтобы туда сохранить данные необходимые для работы сайта.

Однако бывают такие ситуации, когда авторы веб-сайтов могут заполнить и этот объем. Прежде всего, это касается таких случаев, когда разработчики забывают о старых данных и добавляют в хранилище localStorage всё новые и новые порции данных. Вследствие чего через некоторое время объём этих данных может достигнуть лимита хранилища localStorage и новые порции данных уже поступать не будут.

Чтобы этого не произошло при сохранении данных можно указывать дату (штамп времени). А потом, например, при загрузке страницы проверять устарели эти данные или нет. Данные, которые уже устарели удалять. Таким образом, можно поддерживать порядок Ваших записей в localStorage.

Рассмотрим эту ситуацию на примере интернет-магазина в котором описанный выше механизм будет использоваться для сохранения информации о просмотренных пользователем товарах.

// товар, который просматривает пользователь в магазине
var viewItem = {
  id: "5456098",
  name: "Смартфон LG G4",
  dateView: new Date() // дата просмотра товара в интернет-магазине
};
// сохраняем, просматриваемый в данный момент пользователем товар в хранилище
localStorage.setItem (viewItem.id, JSON.stringify(viewItem));

// удаляем старые записи
var key, value;
// перебираем все данные в хранилище
for (var i=0; i<localStorage.length; i++) {
  //получаем ключ записи
  key = localStorage.key(i);
  //получаем по ключу объект
  value = JSON.parse(localStorage.getItem(key));
  // если с момента просмотра товара прошло более чем 30 дней, то удаляем данную запись
  if (Date.parse(value.dateView) < (new Date() - 1000*60*60*24*30)) {
    localStorage.removeItem(key);
  }
}

Программные интерфейсы localStorage и sessionStorage ограничивают доступ к данным тем доменом с учетом протокола и номера порта, в котором находится данная страница. Т.е. данные контейнеров (например: http://itchief.ru) доступны только тем веб-страницам, которые принадлежат этому домену. Страницы, которые не расположены в этом домене (http://itchief.ru) не могут прочитать или удалить данные этих контейнеров.

Рассмотрим быстродействие (в процентном отношении), которое имеют методы при выполнении операций с данными в localStorage и cookie.

В качестве браузеров будем использовать Chrome 47, Firefox 42 и IE11, работающие на операционной системе Windows 7.

Тест 1. Быстродействие методов, осуществляющих чтение данных из localStorage и cookie.

Быстродействие методов, осуществляющих чтение данных из localStorage и cookie

Вывод: Современные браузеры выполняют операции чтения данных из хранилища localStorage намного быстрее, чем из cookie. В браузере Google Chrome это разница достигает нескольких десятков раз, в Firefox – 9 раз и в IE 11 – 2 раза.

Тест 2. Быстродействие методов, осуществляющих запись данных в localStorage и cookie.

Быстродействие методов, осуществляющих запись данных в localStorage и cookie

Вывод: Операции записи данных в хранилище localStorage выполняются быстрее, чем в cookie, но не настолько как при чтении. Браузер Google Chrome выполняет запись в localSorage быстрее в 1.6 раза, Firefox в 7 раз, Internet Explorer 11 показал равнозначный результат.

Тест 3. Быстродействие браузеров, осуществляющих запись данных в localStorage и их чтение.

Быстродействие браузеров, осуществляющих чтение данных из localStorage

Быстродействие браузеров, осуществляющих запись данных в localStorage

Вывод: Firefox показал довольно хорошие преимущества в быстродействии перед другими браузерами, и это касается не только технологии localStorage, но и cookie (диаграммы не приводятся).

# Cookie (Куки, печеньки) localStorage и sessionStorage
Где хранятся? На компьютере пользователя На компьютере пользователя
Доступность данных Данные доступны как со стороны сервера, так и со стороны клиента Данные доступны только со стороны браузера посредством JavaScript API
Максимальный размер данных 4 Кбайт 5 или 10 Мбайт в зависимости от браузера
Удобство методов для работы с данными Неудобные методы для работы с cookie на стороне клиента (браузера) Простые и удобные методы для работы с данными
Время хранения данных ограниченное время (в конце сеанса или по истечении указанной даты) неограниченное время (localStorage) или после завершения сеанса (sessionStorage)
Передаётся ли информация на веб-сервер? Информация всегда передаётся веб-серверу в составе HTTP-заголовка Информация никогда не передаётся на сервер
Браузеры, поддерживающие эту технологию Все Google Chrome 4+, Internet Explorer 8+, Mozilla Firefox 3.5+, Safari 4+, Opera 11.5+
Прочее ЕС требует информировать пользователей, если на сайте используются cookies Безопасность данных обеспечивается технологией HTML5 Origin


   JavaScript и jQuery 0    7706 0

Комментарии (11)

  1. ctac # +1
    Спасибо за статью, но такой информации в сети полно, как работать, для чего нужно и тд. Вы бы провели аналитику, например чем предпочтительней использовать localStorage вместо cookie? Как перестать пользоваться cookie и перейти на localStorage? Я думаю, такой материал был бы куда интересней и полезней
    1. Александр Мальцев # +1
      Спасибо Стас за комментарий. В конце статьи добавил 2 раздела, рассказывающие и показывающие отличия между cookie и Web Storage, а также диаграммы по быстродействию.
    2. Vlad # 0
      Данные из session storage удаляются только после закрытия вкладки, закрытие браузера не удаляет данные
      1. Larisa # 0
        здравствуйте, Александр! спасибо за ваш чудесный блог и оперативные ответы!
        у меня такая проблема: есть боковое меню, открытие/закрытие которого регулируется классами тэга body: site-menubar-unfold / site-menubar-fold соответственно. надо, чтобы при переходе на новую страницу выбранное состояние сохранялось. как это реализовать с помощью sessionStorage я примерно поняла, но как отследить изменение url? нагуглила только функцию hashchange, но «Событие hashchange генерируется когда изменяется идентификатор фрагмента URL (т.е. часть URL следующая за символом #, включая сам символ #).» а у меня меняется не идентификатор, а html-страница. например, localhost/project/idex.html -> localhost/project/stat.html
        спасибо)
        1. Александр Мальцев # 0
          Здравствуйте, Larisa. Спасибо за отзыв. Лучше всего событие «повесить» на момент когда происходит открытие или закрытие бокового меню. И когда это происходит сохранять состояние этого меню в localstorage. А при загрузке новой страницы считывать это состояние и устанавливать его боковому меню.
          1. Larisa # 0
            а каким образом отслеживать изменение страницы?
            1. Larisa # 0
              вот что получилось с хранилищем, но не могу придумать, как прикрутить еще и загрузку новой страницы…
                  // получаем адрес страницы, на которой находимся
                  var pageAddress = location.href; 
              
                  // записываем в хранилище класс, отвечающий за состояние меню
                  if ($('body').hasClass('site-menubar-unfold')){
                    sessionStorage.setItem("menuState","open"); 
                  }
                  else if ($('body').hasClass('site-menubar-fold')){
                    sessionStorage.setItem("menuState","close");
                  }
              
                  //достаем из хранилища класс и, если он отличается от 'site-menubar-unfold', присваиваем его элементу body
                  var menuState = sessionStorage.getItem("menuState");
                  if (menuState == "close"){
                    $("body").removeClass('site-menubar-unfold');
                    $("body").addClass('site-menubar-fold');
                  }
              
              1. Александр Мальцев # 0
                Вам необходимо разобраться каким образом класс site-menubar-unfold или site-menubar-fold появляется у элемента body. Т.е. при каком событии это происходит (например, при клике или как-то по-другому).
                После этого вам необходимо добавить в обработчик этого события блок кода JavaScript, в котором происходит запись информации в хранилище.

                Второй блок, в котором происходит считывание информации, необходимо выполнять после загрузки страницы:
                $(function(){
                  // код, который выполнится после загрузки страницы
                });
                
                1. Larisa # 0
                  классы эти появляются у элемента body по клику, но во время работы на одной странице пользователь может гонять меню туда-сюда сколько угодно — важно, какой класс у элемента body при переходе на новую страницу — т.е. при смене url — и на новую страницу этот же класс нужно перенести…
                  1. Александр Мальцев # 0
                    А что значит при смене url. Пользователь может попасть на страницу посредством поиска, социальных сетей или какой-то ссылки, расположенной на другом сайте. К тому же в JavaScript нет такого события. Поэтому правильно сохранять состояния меню в тот момент, когда пользователь его изменяет. Чтобы в следующий раз, когда пользователь попадёт на сайт, предоставить ему сохранённое состояние.
                    Есть событие, которое происходит при выгрузке страницы, но его поддержка зависит от браузера. Поэтому его не рекомендуется использовать. Применяется оно так:
                    $(window).unload(function() {
                      // сохраняете состояние меню
                    });
                    
                    1. Larisa # 0
                      еще раз большое спасибо вам! действительно, нужно было клик отслеживать)
                      а страница может меняться при переходе по ссылкам внутри нее же или в том же меню
                        //проверяем поменялся ли URL страницы в данной вкладке браузера
                        var pageAddress = location.href;
                        var oldAddress = '';
                        if (localStorage.getItem('pageAddress')) {
                          oldAddress = localStorage.getItem('pageAddress');
                        }
                        localStorage.setItem('pageAddress', pageAddress);
                      
                        var menuState = '';
                        $('#toggleMenubar').click(function(){
                          // записываем в хранилище класс, отвечающий за состояние меню
                          if ($('body').hasClass('site-menubar-unfold')){
                            localStorage.setItem('menuState', 'close');
                          }
                          else if ($('body').hasClass('site-menubar-fold')){
                            localStorage.setItem('menuState', 'open');
                          }
                        });
                        menuState = localStorage.getItem('menuState');
                      
                        if (oldAddress == pageAddress) {
                          //страница не поменялась - делаем действия, которые нам нужны при том же адресе
                          if (menuState == 'close'){
                            $("body").removeClass('site-menubar-unfold');
                            $("body").addClass('site-menubar-fold');
                          }
                        } else {
                          //страница поменялась или открыта впервые - делаем действия, которые нам нужны при новом адресе
                          if (menuState == 'close'){
                            $("body").removeClass('site-menubar-unfold');
                            $("body").addClass('site-menubar-fold');
                          }
                        }
                      

          Вы должны авторизоваться, чтобы оставлять комментарии.