Используем localstorage в react

Веб-хранилище. Назначение localStorage и sessionStorage

Веб-хранилище — это данные, хранящиеся локально в браузере пользователя. Существует 2 типа веб-хранилищ:

  • LocalStorage;
  • SessionStorage.

В них вы можете хранить информацию в формате ключ-значение. Ключ и значение – это всегда строки.

Если мы попытаемся сохранить в значение элемента хранилища другой тип значений, например, объект, то он будет, перед тем как туда записан, преобразован в строку. В данном случае посредством неявного у него вызова метода . Т.е. в значении элемента этих хранилищ кроме строкового типа данных никакого другого содержаться не может.

Отличие между этими хранилищами сводится только к периоду времени, в течение которого они могут хранить данные, помещенные в них:

  • SessionStorage – выполняет это в течение определённого промежутка времени (сессии). Закрытие вкладки или браузера приводит их к удалению. При этом данные в SessionStorage сохраняются при обновлении страницы.
  • LocalStorage – осуществляет это в течение неограниченного времени. Они сохраняются при перезагрузке браузера и компьютера. Их длительность хранения ничем не ограничена. Но, хоть эти данные могут храниться бесконечно в браузере, обычный пользователь может их очень просто удалить, например выполнив очистку истории (при включенной опции «файлы cookie и другие данные сайтов»).

Хранилище LocalStorage похоже на cookies. Оно также применяется для хранения данных на компьютере пользователя (в браузере). Но кроме общих сходств имеется также и много отличий.

Cookies vs. LocalStorage: В чём разница

Отличия между cookies и LocalStorage:

  • по месту хранения (куки и данные LocalStorage хранятся на компьютере пользователя в браузере);
  • по размеру (cookies ограничены 4 Кбайт, а размер LocalStorage — 5 Мбайт);
  • по включению этих данных в HTTP-заголовок (куки в отличие от данных локального хранилища включаются в состав запроса при отправке его на сервер, а также сервер их может добавлять в ответ при отправке его клиенту; таким образом cookies являются частью HTTP-протокола, и увеличивают объём передаваемых данных от клиента серверу и обратно);
  • по доступности данных (печеньки можно прочитать и установить как на сервере, так и на клиенте; на клиенте доступны все куки, кроме тех, у которых установлен флаг ; LocalStorage доступны только в браузере посредством JavaScript API);
  • по времени хранения данных (куки хранятся ограниченное время (до конца сеанса или истечения указанной даты), нахождение данных в локальном хранилище не ограничено по времени);
  • по удобству использования в JavaScript (работа с LocalStorage в JavaScript организовано намного удобнее чем с cookies);
  • по необходимости информирования пользователей Евросоюза (при использовании cookies сайт в ЕС должен получать на это разрешение от пользователей; для данных локального хранилища это не требуется);
  • по назначению (куки в основном используются для управления сеансом, персонализации и отслеживания действий пользователя, в то время как LocalStorage применяется в качестве обычного локального хранилища информации на компьютере пользователя).

Что использовать: LocalStorage или cookies? На самом деле, ответ на этот вопрос очень прост. Если вам не нужно отправлять данные с каждым HTTP-запросом на сервер, то в этом случае лучше использовать для хранения данных LocalStorage.

Безопасность данных

Хранилище LocalStorage привязана к источнику (домену, протоколу и порту). Данные, находящиеся в некотором источнике, доступны для всех сценариев страниц этого же источника. Из сценария, находящегося в одном источнике, нельзя получить доступ к данным, определяемым другим источником.

Хранилище SessionStorage ограничена только одной вкладкой браузера. Это означает, что в сценарии, находящемся в одной вкладке, нельзя получить информацию из сессионного хранилища другой вкладки даже у них одинаковые источники.

Итоги

Основные характеристики LocalStorage и SessionStorage:

  • данные хранятся в виде пар «ключ-значение»;
  • хранить можно только строки;
  • если вам необходимо хранить в этих хранилищах массивы и объекты, то сначала вы должны их превратить в строки, например, используя метод . Для преобразования строки обратно в массив или объект, можно использовать . Подробнее об этом позже.

Builds

Choose which build is right for you!

List of default builds

  • store.everything.min.js: All the plugins, all the storages. Source
  • store.legacy.min.js: Full support for all tested browsers. Add plugins separately. Source
  • store.modern.min.js: Full support for all modern browsers. Add plugins separately. Source
  • store.v1-backcompat.min.js: Full backwards compatibility with store.js v1. Source

Make your own Build

If you’re using npm you can create your own build:

// Example custom build usage:
var engine = require('store/src/store-engine')
var storages = 
	require('store/storages/localStorage'),
	require('store/storages/cookieStorage')

var plugins = 
	require('store/plugins/defaults'),
	require('store/plugins/expire')

var store = engine.createStore(storages, plugins)
store.set('foo', 'bar', new Date().getTime() + 3000) // Using expire plugin to expire in 3 seconds

Storing images

The idea here is to be able to take an image that has been loaded into the current web page and store it into localStorage. As we established above, localStorage only supports strings, so what we need to do here is turn the image into a Data URL. One way to do this for an image, is to load into a canvas element. Then, with a , you can read out the current visual representation in a canvas as a Data URL.

Let’s look at this example where we have an image in the document with an of “elephant”:

// Get a reference to the image element
var elephant = document.getElementById("elephant");

// Take action when the image has loaded
elephant.addEventListener("load", function () {
    var imgCanvas = document.createElement("canvas"),
        imgContext = imgCanvas.getContext("2d");

    // Make sure canvas is as big as the picture
    imgCanvas.width = elephant.width;
    imgCanvas.height = elephant.height;

    // Draw image into canvas element
    imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);

    // Get canvas contents as a data URL
    var imgAsDataURL = imgCanvas.toDataURL("image/png");

    // Save image into localStorage
    localStorage.setItem("elephant", imgAsDataURL);
}, false);

Then, if we want to take it further, we can utilize a JavaScript object and do a date check with localStorage. In this example, we load the image from the server through JavaScript the first time, but for every page load after that, we read the saved image from localStorage instead:

HTML

<figure>
    <img id="elephant" src="about:blank" alt="A close up of an elephant">
    <noscript>
        <img src="elephant.png" alt="A close up of an elephant">
    </noscript>
    <figcaption>A mighty big elephant, and mighty close too!</figcaption>
</figure>

JavaScript

// localStorage with image
var storageFiles = JSON.parse(localStorage.getItem("storageFiles")) || {},
    elephant = document.getElementById("elephant"),
    storageFilesDate = storageFiles.date,
    date = new Date(),
    todaysDate = (date.getMonth() + 1).toString() + date.getDate().toString();

// Compare date and create localStorage if it's not existing/too old
if (typeof storageFilesDate === "undefined" || storageFilesDate < todaysDate) {
    // Take action when the image has loaded
    elephant.addEventListener("load", function () {
        var imgCanvas = document.createElement("canvas"),
            imgContext = imgCanvas.getContext("2d");

        // Make sure canvas is as big as the picture
        imgCanvas.width = elephant.width;
        imgCanvas.height = elephant.height;

        // Draw image into canvas element
        imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);

        // Save image as a data URL
        storageFiles.elephant = imgCanvas.toDataURL("image/png");

        // Set date for localStorage
        storageFiles.date = todaysDate;

        // Save as JSON in localStorage
        localStorage.setItem("storageFiles", JSON.stringify(storageFiles));
    }, false);

    // Set initial image src
    elephant.setAttribute("src", "elephant.png");
}
else {
    // Use image from localStorage
    elephant.setAttribute("src", storageFiles.elephant);
}

Note: A word of warning here is that you might exceed the size available in localStorage, and the best way to control that is using .

Basic Usage

All you need to know to get started:

API

store.js exposes a simple API for cross-browser local storage:

// Store current user
store.set('user', { name:'Marcus' })

// Get current user
store.get('user')

// Remove current user
store.remove('user')

// Clear all keys
store.clearAll()

// Loop over all stored values
store.each(function(value, key) {
	console.log(key, '==', value)
})

Installation

Using npm:

npm i store
// Example store.js usage with npm
var store = require('store')
store.set('user', { name:'Marcus' })
store.get('user').name == 'Marcus'

Using script tag (first download one of the builds):

<!-- Example store.js usage with script tag -->
<script src="path/to/my/store.legacy.min.js"></script>
<script>
store.set('user', { name:'Marcus' })
store.get('user').name == 'Marcus'
</script>

HTML Tutorial

HTML HOMEHTML IntroductionHTML EditorsHTML BasicHTML ElementsHTML AttributesHTML HeadingsHTML ParagraphsHTML StylesHTML FormattingHTML QuotationsHTML CommentsHTML Colors
Colors
RGB
HEX
HSL

HTML CSSHTML Links
Links
Link Colors
Link Bookmarks

HTML Images
Images
Image Map
Background Images
The Picture Element

HTML TablesHTML Lists
Lists
Unordered Lists
Ordered Lists
Other Lists

HTML Block & InlineHTML ClassesHTML IdHTML IframesHTML JavaScriptHTML File PathsHTML HeadHTML LayoutHTML ResponsiveHTML ComputercodeHTML SemanticsHTML Style GuideHTML EntitiesHTML SymbolsHTML EmojisHTML CharsetHTML URL EncodeHTML vs. XHTML

Storage event

When the data gets updated in or , event triggers, with properties:

  • – the key that was changed ( if is called).
  • – the old value ( if the key is newly added).
  • – the new value ( if the key is removed).
  • – the url of the document where the update happened.
  • – either or object where the update happened.

The important thing is: the event triggers on all objects where the storage is accessible, except the one that caused it.

Let’s elaborate.

Imagine, you have two windows with the same site in each. So is shared between them.

You might want to open this page in two browser windows to test the code below.

If both windows are listening for , then each one will react on updates that happened in the other one.

Please note that the event also contains: – the url of the document where the data was updated.

Also, contains the storage object – the event is the same for both and , so references the one that was modified. We may even want to set something back in it, to “respond” to a change.

That allows different windows from the same origin to exchange messages.

Modern browsers also support Broadcast channel API, the special API for same-origin inter-window communication, it’s more full featured, but less supported. There are libraries that polyfill that API, based on , that make it available everywhere.

Открыть базу данных

Для начала работы с IndexedDB нужно открыть базу данных.

Синтаксис:

  • – название базы данных, строка.
  • – версия базы данных, положительное целое число, по умолчанию (объясняется ниже).

У нас может быть множество баз данных с различными именами, но все они существуют в контексте текущего источника (домен/протокол/порт). Разные сайты не могут получить доступ к базам данных друг друга.

После этого вызова необходимо назначить обработчик событий для объекта :

  • : база данных готова к работе, готов «объект базы данных» , его следует использовать для дальнейших вызовов.
  • : не удалось открыть базу данных.
  • : база открыта, но её схема устарела (см. ниже).

IndexedDB имеет встроенный механизм «версионирования схемы», который отсутствует в серверных базах данных.

В отличие от серверных баз данных, IndexedDB работает на стороне клиента, в браузере, и у нас нет прямого доступа к данным. Но когда мы публикуем новую версию нашего приложения, возможно, нам понадобится обновить базу данных.

Если локальная версия базы данных меньше, чем версия, определённая в , то сработает специальное событие , и мы сможем сравнить версии и обновить структуры данных по мере необходимости.

Это событие также сработает, если базы данных ещё не существует, так что в этом обработчике мы можем выполнить инициализацию.

Например, когда мы впервые публикуем наше приложение, мы открываем базу данных с версией и выполняем инициализацию в обработчике :

Когда мы публикуем вторую версию:

Таким образом, в мы обновляем базу данных. Скоро подробно увидим, как это делается. А после того, как этот обработчик завершится без ошибок, сработает .

После у нас есть объект базы данных в , который мы будем использовать для дальнейших операций.

Удалить базу данных:

А что, если открыть предыдущую версию?

Что если мы попробуем открыть базу с более низкой версией, чем текущая? Например, на клиенте база версии 3, а мы вызываем .

Возникнет ошибка, сработает .

Такое может произойти, если посетитель загрузил устаревший код, например, из кеша прокси. Нам следует проверить и предложить ему перезагрузить страницу. А также проверить наши кеширующие заголовки, убедиться, что посетитель никогда не получит устаревший код.

Раз уж мы говорим про версионирование, рассмотрим связанную с этим небольшую проблему.

Проблема заключается в том, что база данных всего одна на две вкладки, так как это один и тот же сайт, один источник. И она не может быть одновременно версии 1 и 2. Чтобы обновить на версию 2, все соединения к версии 1 должны быть закрыты.

Чтобы это можно было организовать, при попытке обновления на объекте базы возникает событие . Нам нужно слушать его и закрыть соединение к базе (а также, возможно, предложить пользователю перезагрузить страницу, чтобы получить обновлённый код).

Если мы его не закроем, то второе, новое соединение будет заблокировано с событием вместо .

Код, который это делает:

Здесь мы делаем две вещи:

  1. Добавляем обработчик после успешного открытия базы, чтобы узнать о попытке параллельного обновления.
  2. Добавляем обработчик для ситуаций, когда старое соединение не было закрыто. Такого не произойдёт, если мы закрываем его в .

Есть и другие варианты. Например, мы можем более «мягко» закрыть соединение в , предложить пользователю сохранить данные перед этим. Новое обновляющее соединение будет заблокировано сразу после того как обработчик завершится, не закрыв соединение, и мы можем в новой вкладке попросить посетителя закрыть старые для обновления.

Такой конфликт при обновлении происходит редко, но мы должны как-то его обрабатывать, хотя бы поставить обработчик , чтобы наш скрипт не «умирал» молча, удивляя посетителя.

Автоматическая фиксация транзакций

В примере выше мы запустили транзакцию и выполнили запрос . Но, как говорилось ранее, транзакция может включать в себя несколько запросов, которые все вместе должны либо успешно завершиться, либо нет. Как нам закончить транзакцию, обозначить, что больше запросов в ней не будет?

Короткий ответ: этого не требуется.

В следующей 3.0 версии спецификации, вероятно, будет возможность вручную завершить транзакцию, но сейчас, в версии 2.0, такой возможности нет.

Когда все запросы завершены и очередь микрозадач пуста, тогда транзакция завершится автоматически.

Как правило, это означает, что транзакция автоматически завершается, когда выполнились все её запросы и завершился текущий код.

Таким образом, в приведённом выше примере не требуется никакой специальный вызов, чтобы завершить транзакцию.

Такое автозавершение транзакций имеет важный побочный эффект. Мы не можем вставить асинхронную операцию, такую как или в середину транзакции. IndexedDB никак не заставит транзакцию «висеть» и ждать их выполнения.

В приведённом ниже коде в запросе в строке с будет ошибка, потому что транзакция уже завершена, больше нельзя выполнить в ней запрос:

Всё потому, что является асинхронной операцией, макрозадачей. Транзакции завершаются раньше, чем браузер приступает к выполнению макрозадач.

Авторы спецификации IndexedDB из соображений производительности считают, что транзакции должны завершаться быстро.

В частности, транзакции «блокируют» хранилища от записи. Таким образом, если одна часть приложения инициирует транзакцию в хранилище объектов , то другая часть приложения, которая хочет сделать то же самое, должна ждать: новая транзакция «зависает» до завершения первой. Это может привести к странным задержкам, если транзакции слишком долго выполняются.

Что же делать?

В приведённом выше примере мы могли бы запустить новую транзакцию перед новым запросом .

Но ещё лучше выполнять операции вместе, в рамках одной транзакции: отделить транзакции IndexedDB от других асинхронных операций.

Сначала сделаем , подготовим данные, если нужно, затем создадим транзакцию и выполним все запросы к базе данных.

Чтобы поймать момент успешного выполнения, мы можем повесить обработчик на событие :

Только гарантирует, что транзакция сохранена целиком. По отдельности запросы могут выполниться, но при финальной записи что-то может пойти не так (ошибка ввода-вывода, проблема с диском, например).

Чтобы вручную отменить транзакцию, выполните:

Это отменит все изменения, сделанные запросами в транзакции, и сгенерирует событие .

Overview #

This API has been optimized to meet the specific storage needs of extensions. It provides the same storage capabilities as the localStorage API with the following key differences:

  • User data can be automatically synced with Chrome sync (using ).
  • Your extension’s content scripts can directly access user data without the need for a background page.
  • A user’s extension settings can be persisted even when using split incognito behavior.
  • It’s asynchronous with bulk read and write operations, and therefore faster than the blocking and serial .
  • User data can be stored as objects (the stores data in strings).
  • Enterprise policies configured by the administrator for the extension can be read (using with a schema).

What is the Web Storage API?

The Web Storage API is a set of mechanisms that enable browsers to store key-value pairs. It is designed to be much more intuitive than using cookies.

The key-value pairs represent storage objects, which are similar to objects except they remain intact during page loads, and are always strings. You can access these values like an object or using the method (more on that later).

What is the difference between and ?

The Web Storage API consists of two mechanisms: and . Both and maintain a separate storage area for each available origin for the duration of the page session.

The main difference between and is that only maintains a storage area while the browser is open (including when the page reloads or restores) while continues to store data after the browser is closed. In other words, whereas data stored in is cleared when the page is closed, data stored in does not expire.

In this tutorial, we’ll focus on how to use in JavaScript.

Configuration

setPrefix

You could set a prefix to avoid overwriting any local storage variables from the rest of your appDefault prefix:

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setPrefix('yourAppName');
});

setStorageType

You could change web storage type to localStorage or sessionStorageDefault storage:

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageType('sessionStorage');
});

setDefaultToCookie

If localStorage is not supported, the library will default to cookies instead. This behavior can be disabled.Default:

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setDefaultToCookie(false);
});

setStorageCookie

Set cookie options (usually in case of fallback)expiry: number of days before cookies expire (0 = session cookie). default: path: the web path the cookie represents. default: secure: whether to store cookies as secure. default:

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageCookie(45, '<path>', false);
});

setStorageCookieDomain

Set the cookie domain, since this runs inside a the block, only providers and constants can be injected. As a result, service can’t be used here, use a hardcoded string or .No default value

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageCookieDomain('<domain>');
});

For local testing (when you are testing on localhost) set the domain to an empty string ». Setting the domain to ‘localhost’ will not work on all browsers (eg. Chrome) since some browsers only allow you to set domain cookies for registry controlled domains, i.e. something ending in .com or so, but not IPs or intranet hostnames like localhost.

setNotify

Configure whether events should be broadcasted on $rootScope for each of the following actions:setItem , default: , event «LocalStorageModule.notification.setitem»removeItem , default: , event «LocalStorageModule.notification.removeitem»

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setNotify(true, true);
});

Configuration Example

Using all together

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setPrefix('myApp')
    .setStorageType('sessionStorage')
    .setNotify(true, true)
});

Интегрируем локальное хранилище

Сейчас мы добавим немного функционала в наше приложение. Во первых, каждый раз, когда форму будут сабмитить, значение из  будет добавлено в  вместе с мгновенным отображением в списке задач. Также, нам нужно будет пробежаться циклом по всему локальному хранилищу и показать элементы этого хранилища сверху списка существующих задач. Ну и под конец, мы добавим кнопку “Clear All”, которая удалит все элементы не только из локального хранилища, но и из списка .

Давайте сначала создадим пустой массив и создадим в  ключ под названием .  в виде значений ключей принимает только строки, к тому же, нам надо хранить задачи в массиве.

Мы можем обойти это ограничение с помощью , чтобы конвертировать массив в строку. А уже потом мы применим  для конвертирования содержимого  в какой-нибудь рабочий формат, который мы положим в переменную . Вставьте этот код под всем, что мы написали выше.

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

Далее, мы собираемся пробежаться по всему содержимому переменной , которая содержит всё из нашего  в таком виде, в котором мы можем работать с этими данным в JavaScript. Затем мы заново запустим . Это покажет нам всю нужную информацию в списке при каждом открытии приложения.

И под конец мы добавим событие по клику на кнопку, которая очистит все данные из  и удалит все потомков у .

Если всё пройдет хорошо, то данные запишутся в хранилище и покажутся в списке задач, а само хранилище вы сможете проверить введя  в консоли.

Но у нас осталась последняя проблема. После закрытия браузера или перезагрузки страницы, вся существующая информация в  пропадает и ничего не остается на нашем фронте. Почему?

Потому, что наш  сбрасывается каждый раз при запуске скрипта. Мы можем пофиксить эту проблемку, создав условие, которое проверит наличие доступного :

Практичнее было бы использовать тернарный оператор.

Теперь наше приложение завершено. И когда вы вводите какую-либо информацию, а затем обновляете или закрываете окно браузера, данные будут на месте, пока вы самостоятельно не очистите их в настройках браузера или запустив команду . Ну а вот и полный код JavaScript.

Приватный просмотр (режим инкогнито)

Большинство современных браузеров поддерживают опцию конфиденциальности «инкогнито», «приватный просмотр», или что-то подобное, что не хранит такие данные, как история и файлы cookie. Это принципиально несовместимо с веб-хранилищем по очевидным причинам, по этой причине поставщики браузеров экспериментируют с различными сценариями устранения этой несовместимости.

Большинство браузеров выбрали стратегию, в которой API хранилища по-прежнему доступны и кажутся полностью функциональными, с той большой разницей, что все сохраненные данные стираются после закрытия браузера. Для этих браузеров все еще существуют различные интерпретации того, что должно быть сделано с существующими сохраненными данными (из обычной сессии просмотра). Затем есть некоторые браузеры, например, Safari, которые выбрали такое решение при котором хранилище доступно, но пусто и имеет квоту 0 байт, что фактически делает невозможным запись в него данных.

Разработчики должны знать об этих различных реализациях и учитывать их при разработке веб-сайтов в зависимости от API веб-хранилища.

Итого

IndexedDB можно рассматривать как «localStorage на стероидах». Это простая база данных типа ключ-значение, достаточно мощная для оффлайн приложений, но простая в использовании.

Лучшим руководством является спецификация, текущая версия 2.0, но также поддерживаются несколько методов из 3.0 (не так много отличий) версии.

Использование можно описать в нескольких фразах:

  1. Подключить обёртку над промисами, например idb.
  2. Открыть базу данных:
    • Создание хранилищ объектов и индексов происходит в обработчике .
    • Обновление версии – либо сравнивая номера версий, либо можно проверить что существует, а что нет.
  3. Для запросов:
    • Создать транзакцию (можно указать readwrite, если надо).
    • Получить хранилище объектов .
  4. Затем для поиска по ключу вызываем методы непосредственно у хранилища объектов.
  5. Если данные не помещаются в памяти, то используйте курсор.

Демо-приложение:

Результат
index.html

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector