10 вариантов хранения на стороне клиента и когда их использовать

10 вариантов хранения на стороне клиента и когда их использовать Изучение

Хранение данных и управление ими в браузере — также известное как хранилище на стороне клиента — полезно, когда не нужно или нецелесообразно отправлять их на веб-сервер.

Ситуации для хранения и обработки данных в браузере включают:

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

Вот десять вариантов хранения данных браузера:

  1. Переменные JavaScript
  2. Хранилище узлов DOM
  3. Интернет-хранилище( localStorageи sessionStorage)
  4. IndexedDB
  5. Cache API(не используйте AppCache !)
  6. API доступа к файловой системе
  7. API записей файлов и каталогов
  8. Cookies
  9. Name
  10. WebSQL

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

Прежде чем мы перейдем к вариантам, сделаем небольшое примечание о сохранении данных…

Сохранение данных

Как правило, данные, которые вы храните, будут:

  1. постоянный: он остается до тех пор, пока ваш код не решит удалить его, или
  2. volatile: он остается до завершения сеанса браузера, обычно, когда пользователь закрывает вкладку

Реальность более тонкая.

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

Браузеры также записывают состояние страницы. Вы можете уйти с сайта и щелкнуть назад или закрыть и снова открыть вкладку; страница должна выглядеть идентично. Переменные и данные, считающиеся доступными только для сеанса, по-прежнему доступны.

1. Переменные JavaScript

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

Сохранение состояния в переменных JavaScript — самый быстрый и простой вариант. Я уверен, что вам не нужен пример, но…

const
  a = 1,
  b = 'two',
  state = {
    msg:  'Hello',
    name: 'Craig'
  };

Преимущества:

  • легко использовать
  • быстрый
  • нет необходимости в сериализации или десериализации

Недостатки:

  • хрупкий: обновление или закрытие вкладки стирает все
  • сторонние скрипты могут проверять или перезаписывать значения global ( window)

Вы уже используете переменные. Вы можете рассмотреть возможность постоянного сохранения состояния переменной при выгрузке страницы.

2. Хранилище узлов DOM

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

Большинство элементов DOM на странице или в памяти могут хранить значения в именованных атрибутах. Безопаснее использовать имена атрибутов с префиксом data-:

  1. Атрибут никогда не будет иметь связанных функций HTML
  2. Вы можете получить доступ к значениям с помощью datasetсобственности, а не больше .setAttribute()и .getAttribute()методов.

Значения хранятся в виде строк, поэтому может потребоваться сериализация и десериализация. Например:

// locate <main> element
const main = document.querySelector('main');

// store values
main.dataset.value1 = 1;
main.dataset.state = JSON.stringify({ a:1, b:2 });

// retreive values
console.log( main.dataset.value1 ); // "1"
console.log( JSON.parse(main.dataset.state).a ); // 1

Преимущества:

  • вы можете определять значения в JavaScript или HTML, например <main data-value1=»1″>
  • полезно для хранения состояния конкретного компонента
  • DOM работает быстро! (вопреки распространенному мнению)

Недостатки:

  • хрупкий: обновление или закрытие вкладки стирает все (если значение не отображается на сервере в HTML)
  • Только строки: требуется сериализация и десериализация
  • более крупный DOM влияет на производительность
  • сторонние скрипты могут проверять или перезаписывать значения

Хранилище узлов DOM работает медленнее, чем переменные. Используйте его экономно в ситуациях, когда удобно хранить состояние компонента в HTML.

3. Web Storage (localStorage and sessionStorage)

метрика комментарий
вместимость 5 МБ на домен
скорость чтения / записи синхронная работа: может быть медленной
упорство данные остаются до тех пор, пока не будут удалены

Веб-хранилище предоставляет два похожих API для определения пар имя / значение. Использовать:

  1. localStorageдля хранения постоянных данных и
  2. window.sessionStorageдля сохранения данных только сеанса, пока вкладка браузера остается открытой

Храните или обновляйте именованные элементы с помощью .setItem():

localStorage.setItem('value1', 123);
localStorage.setItem('value2', 'abc');
localStorage.setItem('state', JSON.stringify({ a:1, b:2, c:3 }));

Получите их с помощью . getItem():

const state = JSON.parse( localStorage.getItem('state') );

И удалите их с помощью . removeItem():

localStorage.removeItem('state')

Другие свойства и методы включают:

  • .length: количество хранимых элементов
  • .key(N): имя N-го ключа
  • .clear(): удалить все сохраненные элементы

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

window.addEventListener('storage', s => {

  console.log(`item changed: ${ s.key }`);
  console.log(`from value  : ${ s.oldValue }`);
  console.log(`to new value: ${ s.newValue }`);

});

Преимущества:

  • API простой пары имя / значение
  • параметры сеанса и постоянного хранения
  • хорошая поддержка браузера

Недостатки:

  • Только строки: требуется сериализация и десериализация
  • неструктурированные данные без транзакций, индексации или поиска
  • синхронный доступ повлияет на производительность больших наборов данных

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

4. IndexedDB

метрика комментарий
вместимость зависит от устройства. Не менее 1 ГБ, но может составлять до 60% оставшегося дискового пространства
скорость чтения / записи быстрый
упорство данные остаются до тех пор, пока не будут удалены

IndexedDB предлагает низкоуровневый API, похожий на NoSQL, для хранения больших объемов данных. Хранилище можно индексировать, обновлять с помощью транзакций и выполнять поиск с помощью асинхронных методов.

API IndexedDB сложен и требует некоторого манипулирования событиями. Следующая функция открывает соединение с базой данных при передаче имени, номера версии и дополнительной функции обновления (вызываемой при изменении номера версии):

// connect
function dbConnect(dbName, version, upgrade) {

  return new Promise((resolve, reject) => {

    const request = indexedDB.open(dbName, version);

    request.onsuccess = e => {
      resolve(e.target.result);
    };

    request.onerror = e => {
      console.error(`indexedDB error: ${ e.target.errorCode }`);
    };

    request.onupgradeneeded = upgrade;

  });

}

Следующий код подключается к myDBбазе данных и инициализирует todoхранилище объектов (аналогично таблице SQL или коллекции MongoDB). Затем он определяет автоматически увеличивающийся ключ с именем id:

(async () => {

  const db = await dbConnect('myDB', 1.0, e => {

    db = e.target.result;
    const store = db.createObjectStore('todo', { keyPath: 'id', autoIncrement: true });

  })

})();

Как только dbсоединение будет готово, вы можете.addдобавить новые элементы данных в транзакцию:

db.transaction(['todo'], 'readwrite')
  .objectStore('todo')
  .add({ task: 'do something' })
  .onsuccess = () => console.log( 'added' );

И вы можете получить значения, такие как первый элемент:

db.transaction(['todo'], 'readonly')
  .objectStore('todo')
  .get(1)
  .onsuccess = data => console.log( data.target.result );
  // { id: 1, task: 'do something' }

Преимущества:

  • гибкое хранилище данных с самым большим пространством
  • надежные транзакции, возможности индексации и поиска
  • хорошая поддержка браузера

Недостатки:

  • сложный обратный вызов и API на основе событий

IndexedDB — лучший вариант для надежного хранения больших объемов данных, но вам понадобится библиотека-оболочка, такая как idb, Dexie.js или JsStore.

5. API кеширования

метрика комментарий
вместимость зависит от устройства, но Safari ограничивает каждый домен до 50 МБ
скорость чтения / записи быстрый
упорство данные остаются до очистки или через две недели в Safari

API — кэша обеспечивает хранение для запроса HTTP и пар объектов ответа. Вы можете создать любое количество именованных кэшей для хранения любого количества элементов сетевых данных.

API обычно используется в сервис-воркерах для кэширования сетевых ответов для прогрессивных веб-приложений. Когда устройство отключается от сети, кэшированные ресурсы могут быть повторно обслужены, чтобы веб-приложение могло работать в автономном режиме.

Следующий код сохраняет сетевой ответ в кэше с именем myCache:

// cache name
const cacheName = 'myCache';

(async () => {

  // cache network response
  const stored = await cacheStore('/service.json') );
  console.log(stored ? 'stored OK' : 'store failed');

})();

// store request
async function cacheStore( url ) {

  try {

    // open cache
    const cache = await caches.open( cacheName );

    // fetch and store response
    await cache.add( url );
    return true;

  }
  catch(err) {
    return undefined; // store failed
  }

}

Аналогичная функция может получить элемент из кеша. В этом примере он возвращает основной текст ответа:

(async () => {

  // fetch text from cached response
  const text = await cacheGet('/service.json') );
  console.log( text );

})();

async function cacheGet( url ) {

  try {

    const

      // open cache
      cache = await caches.open( cacheName ),

      // fetch stored response
      resp = await cache.match(url);

    // return body text
    return await resp.text();

  }
  catch(err) {
    return undefined; // cache get failed
  }

}

Преимущества:

  • хранит любой сетевой ответ
  • может улучшить производительность веб-приложений
  • позволяет веб-приложению работать в автономном режиме
  • современный API на основе обещаний

Недостатки:

  • не практично для хранения состояния приложения
  • возможно, менее полезен за пределами прогрессивных веб-приложений
  • Apple недоброжелательно относится к PWA и Cache API

Cache API — лучший вариант для хранения файлов и данных, полученных из сети. Вероятно, вы могли бы использовать его для хранения состояния приложения, но он не предназначен для этой цели, и есть варианты получше.

AppCache был несуществующим предшественником Cache API. Это не то решение для хранения, которое вы ищете. Здесь ничего нет. Пожалуйста, двигайтесь дальше.

6. API доступа к файловой системе

метрика комментарий
вместимость зависит от оставшегося места на диске
скорость чтения / записи зависит от файловой системы
упорство данные остаются до тех пор, пока не будут удалены

File System Access API позволяет браузер для чтения, записи, изменять и удалять файлы из локальной файловой системы. Браузеры работают в изолированной среде, поэтому пользователь должен предоставить разрешение на определенный файл или каталог. Это возвращает FileSystemHandle, чтобы веб-приложение могло читать или записывать данные, как настольное приложение.

Следующая функция сохраняет Blob- объект в локальный файл:

async function save( blob ) {

  // create handle to a local file chosen by the user
  const handle = await window.showSaveFilePicker();

  // create writable stream
  const stream = await handle.createWritable();

  // write the data
  await stream.write(blob);

  // save and close the file
  await stream.close();
}

Преимущества:

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

Недостатки:

  • минимальная поддержка браузера (только Chrome)
  • API может измениться

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

7. API записей файлов и каталогов

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

Файлы и каталоги Запись API предоставляют песочницы файловой системы доступной для домена, который может создавать, писать, читать и удалять каталоги и файлов.

Преимущества:

  • может иметь несколько интересных применений

Недостатки:

  • нестандартные, несовместимость между реализациями и поведение могут измениться.

MDN прямо заявляет: не используйте это на производственных сайтах. Широкая поддержка будет в лучшем случае через несколько лет.

метрика комментарий
вместимость 80 КБ на домен (20 файлов cookie размером до 4 КБ в каждом)
скорость чтения / записи быстрый
упорство хорошо: данные остаются до тех пор, пока они не будут удалены или не истечет

Файлы cookie — это данные, относящиеся к домену. Они имеют репутацию отслеживающих людей, но они необходимы для любой системы, которая должна поддерживать состояние сервера, например, для входа в систему. В отличие от других механизмов хранения, файлы cookie (обычно) передаются между браузером и сервером при каждом HTTP-запросе и ответе. Оба устройства могут проверять, изменять и удалять данные cookie.

document.cookieустанавливает значения cookie в клиентском JavaScript. Вы должны определить строку с именем и значением, разделенными символом равенства ( =). Например:

document.cookie = 'cookie1=123';
document.cookie = 'anothercookie=abc';

Значения не должны содержать запятых, точек с запятой или пробелов, поэтому encodeURIComponent()может потребоваться:

document.cookie = `hello=${ encodeURIComponent('Hello, everyone!') }`;

К дополнительным настройкам файлов cookie можно добавить разделители через точку с запятой, в том числе:

  • ;domain=: если не установлен, cookie доступен только в текущем домене URL. Использование ;path=mysite.comразрешило бы это на любом поддомене mysite.com.
  • ;path=: если не установлен, cookie доступен только в текущем и дочерних путях. Установите, ;path=/чтобы разрешить любой путь в домене.
  • ;max-age=: время истечения срока действия cookie в секундах, например ;max-age=60.
  • ;expires=: дата истечения срока действия cookie, например ;expires=Thu, 04 July 2021 10:34:38 UTC(используйте date.toUTCString()для соответствующего форматирования).
  • ;secure: cookie будет передаваться только по HTTPS.
  • ;HTTPOnly: делает файлы cookie недоступными для клиентского JavaScript.
  • ;samesite=: определяет, может ли другой домен получить доступ к cookie. Установите для него значение lax(по умолчанию, использует файл cookie для текущего домена), strict(останавливает отправку исходного файла cookie при переходе по ссылке из другого домена) или none(без ограничений).

Пример: установить файл cookie состояния, срок действия которого истекает через 10 минут и доступен по любому пути в текущем домене:

const state = { a:1, b:2, c:3 };

document.cookie = `state=${ encodeURIComponent(JSON.stringify(state)) }; path=/; max=age=600`;

document.cookieвозвращает строку, содержащую каждую пару имени и значения, разделенную точкой с запятой. Например:

console.log( document.cookie );
// "cookie1=123; anothercookie=abc; hello=Hello%2C%20everyone!; state=%7B%22a%22%3A1%2C%22b%22%3A2%2C%22c%22%3A3%7D"

Функция ниже анализирует строку и преобразует ее в объект, содержащий пары имя-значение. Например:

const
  cookie = cookieParser();
  state = cookie.state;

console.log( state );
// { a:1, b:2, c:3 }


// parse cookie values
function cookieParser() {

  const nameValue = {};

  document.cookie
    .split('; ')
    .map(nv => {

      nv = nv.split('=');
      if (nv[0]) {

        let v = decodeURIComponent( nv[1] || '' );

        try { v = JSON.parse(v); }
        catch(e){}

        nameValue[ nv[0] ] = v;

      }

    })

  return nameValue;

}

Преимущества:

  • надежный способ сохранить состояние между клиентом и сервером
  • ограничен доменом и, необязательно, путем
  • автоматический контроль истечения срока действия с помощью max-age(секунд) или Expires(дата)
  • используется в текущем сеансе по умолчанию (установите дату истечения срока, чтобы данные сохранялись после обновления страницы и закрытия вкладки)

Недостатки:

  • файлы cookie часто блокируются браузерами и плагинами (обычно они конвертируются в файлы cookie сеанса, поэтому сайты продолжают работать)
  • неуклюжая реализация JavaScript (лучше всего создать свой собственный обработчик файлов cookie или выбрать библиотеку, такую ​​как js-cookie )
  • только строки (требуется сериализация и десериализация)
  • ограниченное место для хранения
  • файлы cookie могут быть проверены сторонними скриптами, если вы не ограничили доступ
  • обвиняется в нарушении конфиденциальности (региональное законодательство может требовать от вас показывать предупреждение о несущественных файлах cookie)
  • данные cookie добавляются к каждому HTTP-запросу и ответу, что может повлиять на производительность (хранение 50 КБ данных cookie, а затем запрос десяти 1-байтовых файлов
  • потребует один мегабайт полосы пропускания)

Избегайте файлов cookie, если нет реальной альтернативы.

9. window.name

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

В window.nameсобственности устанавливает и получает имя контекста просмотра окна. Вы можете установить одно строковое значение, которое будет сохраняться между обновлениями браузера или установкой ссылки в другом месте и обратным щелчком. Например:

let state = { a:1, b:2, c:3 };
window.name = JSON.stringify( state );

Изучите значение, используя:

state = JSON.parse( window.name );
console.log( state.b );
// 2

Преимущества:

  • легко использовать
  • может использоваться для данных только сеанса

Недостатки:

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

window.nameникогда не предназначался для хранения данных. Это взлом, и есть варианты получше.

10. WebSQL

метрика комментарий
вместимость 5 МБ на домен
скорость чтения / записи вялый
упорство данные остаются до тех пор, пока не будут удалены

WebSQL был попыткой перенести в браузер хранилище баз данных, подобное SQL. Пример кода:

// create DB (name, version, description, size in bytes)
const db = openDatabase('todo', '1.0', 'my to-do list', 1024 * 1024);

// create table and insert first item
db.transaction( t => {

  t.executeSql('CREATE TABLE task (id unique, name)');
  t.executeSql('INSERT INTO task (id,name) VALUES (1, "wash cat")');

});

// output array of all items
db.transaction( t => {

  t.executeSql(
    "SELECT * FROM task",
    [],
    (t, results) => { console.log(results.rows); }
  );

});

Chrome и некоторые выпуски Safari поддерживают эту технологию, но против нее выступили Mozilla и Microsoft в пользу IndexedDB.

Преимущества:

  • разработан для надежного хранения и доступа к данным на стороне клиента
  • знакомый синтаксис SQL, часто используемый серверными разработчиками

Недостатки:

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

Не используйте WebSQL! Это не было жизнеспособным вариантом с тех пор, как спецификация устарела в 2010 году.

Тщательная проверка хранилища

API хранения может исследовать пространство, доступное для веб — хранилище, индексированной, и API кэша. Все браузеры, кроме Safari и IE, поддерживают API на основе обещаний, который предлагает .estimate()метод для вычисления quota(пространства, доступного для домена) и usage(пространства, уже использованного). Например:

(async () => {

  if (!navigator.storage) return;

  const storage = await navigator.storage.estimate();

  console.log(`bytes allocated  : ${ storage.quota }`);
  console.log(`bytes in use     : ${ storage.usage }`);

  const pcUsed = Math.round((storage.usage / storage.quota) * 100);
  console.log(`storage used     : ${ pcUsed }%`);

  const mbRemain = Math.floor((storage.quota - storage.usage) / 1024 / 1024);
  console.log(`storage remaining: ${ mbRemain } MB`);

})();

Доступны еще два асинхронных метода:

  • .persist(): возвращается, trueесли у сайта есть разрешение на хранение постоянных данных, и
  • .persisted(): возвращается, trueесли сайт уже сохранил постоянные данные

Панель » Приложение» в инструментах разработчика браузера ( в Firefox называется » Хранилище» ) позволяет просматривать, изменять и очищать localStorage, sessionStorage, IndexedDB, WebSQL, файлы cookie и хранилище кеша.

Вы также можете проверить данные cookie, отправленные в заголовках HTTP-запроса и ответа, щелкнув любой элемент на панели » Сеть» инструментов разработчика.

Storage Smorgasbord

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

Читайте также:  Указатель C++ на указатель (двойной указатель)
Оцените статью
bestprogrammer.ru
Добавить комментарий

  1. Александр

    Спасибо!

    Ответить