Хранилище для Интернета

Существует множество различных вариантов хранения данных в браузере. Какой из них лучше всего соответствует вашим потребностям?

Интернет-соединения могут быть нестабильными или вообще отсутствовать в пути, поэтому автономная поддержка и надежная производительность являются общими характеристиками прогрессивных веб-приложений . Даже в идеальных беспроводных средах разумное использование кэширования и других методов хранения может существенно улучшить взаимодействие с пользователем. Существует несколько способов кэширования статических ресурсов приложения (HTML, JavaScript, CSS, изображений и т. д.) и данных (пользовательских данных, новостных статей и т. д.). Но какое решение является лучшим? Сколько вы можете хранить? Как предотвратить его выселение?

Что мне следует использовать?

Вот общие рекомендации по хранению ресурсов:

IndexedDB, OPFS и API хранилища кэша поддерживаются в каждом современном браузере. Они асинхронны и не блокируют основной поток (но существует также синхронный вариант OPFS, доступный исключительно в веб-воркерах). Они доступны из объекта window , веб-воркеров и сервис-воркеров, что позволяет использовать их в любом месте вашего кода.

А как насчет других механизмов хранения?

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

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

Следует избегать использования LocalStorage , поскольку он синхронен и блокирует основной поток. Его размер ограничен примерно 5 МБ и может содержать только строки. LocalStorage недоступен для веб-работников или сервисных работников.

Файлы cookie имеют свое назначение, но их не следует использовать для хранения. Файлы cookie отправляются с каждым HTTP-запросом, поэтому хранение большего количества данных, чем небольшой объем данных, значительно увеличит размер каждого веб-запроса. Они синхронны и недоступны веб-воркерам. Подобно LocalStorage и SessionStorage, файлы cookie ограничиваются только строками.

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

API файловой системы и API FileWriter предоставляют методы для чтения и записи файлов в изолированную файловую систему. Хотя он является асинхронным, его не рекомендуется использовать, поскольку он доступен только в браузерах на базе Chromium .

Сколько я могу хранить?

Короче говоря, очень много , как минимум пара сотен мегабайт, а потенциально и сотни гигабайт и более. Реализации браузеров различаются, но объем доступного хранилища обычно зависит от объема хранилища, доступного на устройстве.

  • Chrome позволяет браузеру использовать до 80% общего дискового пространства. Источник может использовать до 60% общего дискового пространства. Вы можете использовать API StorageManager , чтобы определить максимальную доступную квоту. Другие браузеры на базе Chromium могут отличаться.
    • В режиме инкогнито Chrome уменьшает объем памяти, которую может использовать источник, примерно до 5 % от общего дискового пространства.
    • Если пользователь включил в Chrome «Очистить файлы cookie и данные сайта при закрытии всех окон», квота хранилища значительно уменьшается до максимального значения, примерно 300 МБ.
  • Firefox позволяет браузеру использовать до 50% свободного дискового пространства. Группа eTLD+1 (например, example.com , www.example.com и foo.bar.example.com ) может использовать до 2 ГБ . Вы можете использовать API StorageManager , чтобы определить, сколько места еще доступно.
  • Safari (как для настольных компьютеров, так и для мобильных устройств) позволяет использовать около 1 ГБ. Когда лимит будет достигнут, Safari предложит пользователю увеличить лимит с шагом 200 МБ. Мне не удалось найти никакой официальной документации по этому поводу.
    • Если PWA добавляется на главный экран мобильного Safari, создается новый контейнер хранения, и между PWA и мобильным Safari ничего не используется совместно. После достижения квоты для установленного PWA, похоже, нет никакого способа запросить дополнительное хранилище.

Раньше, если сайт превышал определенный порог хранимых данных, браузер предлагал пользователю предоставить разрешение на использование большего количества данных. Например, если источник использовал более 50 МБ, браузер предложит пользователю разрешить сохранение до 100 МБ, а затем повторит запрос с шагом 50 МБ.

Сегодня большинство современных браузеров не запрашивают пользователя и позволяют сайту использовать выделенную ему квоту. Исключением является Safari, который при превышении квоты хранилища выдает запрос на разрешение увеличить выделенную квоту. Если источник попытается использовать больше выделенной ему квоты, дальнейшие попытки записи данных потерпят неудачу.

Как я могу проверить, сколько места доступно?

Во многих браузерах вы можете использовать API StorageManager , чтобы определить объем хранилища, доступный источнику, и объем хранилища, который он использует. Он сообщает общее количество байтов, используемых IndexedDB и Cache API, а также позволяет рассчитать приблизительный оставшийся доступный объем памяти.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

Вы должны обнаружить ошибки превышения квоты (см. ниже). В некоторых случаях доступная квота может превысить фактический объем доступного хранилища.

Осмотреть

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

В Chrome 88 была добавлена ​​новая функция, которая позволяет вам переопределить квоту хранилища сайта на панели «Хранилище». Эта функция дает вам возможность моделировать различные устройства и тестировать поведение ваших приложений в сценариях с нехваткой дискового пространства. Перейдите в раздел «Приложение» , затем «Хранилище» , установите флажок «Имитировать пользовательскую квоту хранилища» и введите любое допустимое число для имитации квоты хранилища.

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

Как справиться с превышением квоты?

Что делать, если превышена квота? Самое главное, вы всегда должны ловить и обрабатывать ошибки записи, будь то QuotaExceededError или что-то еще. Затем, в зависимости от дизайна вашего приложения, решите, как с ним справиться. Например, удалите контент, к которому не было доступа в течение длительного времени, удалите данные в зависимости от размера или предоставьте пользователям возможность выбирать, что они хотят удалить.

И IndexedDB, и Cache API выдают DOMError с именем QuotaExceededError , когда вы превысили доступную квоту.

ИндекседБД

Если источник превысил свою квоту, попытки записи в IndexedDB завершится неудачей. Будет вызван обработчик транзакции onabort() с передачей события. Событие будет включать DOMException в свойство error. Проверка name ошибки вернет QuotaExceededError .

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

API кэша

Если источник превысил свою квоту, попытки записи в Cache API будут отклонены с исключением QuotaExceededError DOMException .

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

Как происходит выселение?

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

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

Политика выселения в духе лучших усилий заключается в следующем:

  • Браузеры на основе Chromium начнут удалять данные, когда в браузере заканчивается место, сначала удаляя все данные сайта из наименее использованного источника, затем из следующего, пока браузер не перестанет превышать лимит.
  • Firefox начнет удалять данные, когда доступное дисковое пространство будет заполнено, сначала удаляя все данные сайта из наименее использованного источника, затем из следующего, пока браузер не перестанет превышать лимит.
  • Раньше Safari не удалял данные, но недавно ввел новое семидневное ограничение на все записываемые хранилища (см. ниже).

Начиная с iOS и iPadOS 13.4, а также Safari 13.1 на macOS, существует семидневное ограничение на все хранилище, доступное для записи скриптов, включая IndexedDB, регистрацию сервисных работников и Cache API. Это означает, что Safari удалит весь контент из кеша через семь дней использования Safari, если пользователь не взаимодействует с сайтом. Эта политика вытеснения не применяется к установленным PWA , добавленным на главный экран. Подробную информацию см. в разделе «Полная блокировка сторонних файлов cookie и многое другое» в блоге WebKit.

Ведра для хранения

Основная идея API Storage Buckets — предоставить сайтам возможность создавать несколько сегментов хранения, при этом браузер может удалить каждый сегмент независимо от других сегментов. Это позволяет разработчикам указывать приоритеты вытеснения, чтобы гарантировать, что наиболее ценные данные не будут удалены.

Бонус: зачем использовать оболочку для IndexedDB

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

Бонус: SQLite Wasm

После того, как Web SQL был объявлен устаревшим и удален из Chrome, Google работал с сопровождающими популярной базы данных SQLite, чтобы предложить замену Web SQL на основе SQLite. Прочтите SQLite Wasm в браузере, поддерживаемом частной файловой системой Origin, чтобы узнать, как его использовать.

Заключение

Прошли времена ограниченного объема памяти и необходимости хранить все больше и больше данных. Сайты могут эффективно хранить все ресурсы и данные, необходимые для работы. Используя API StorageManager, вы можете определить, какой объем вам доступен и какой объем вы уже использовали. А с постоянным хранилищем , если пользователь не удалит его, вы можете защитить его от выселения.

Дополнительные ресурсы

Спасибо

Особая благодарность Джарриду Гудману, Филу Уолтону, Эйдзи Китамуре, Дэниелу Мерфи, Дарвину Хуангу, Джошу Беллу, Марин Круйссельбринк и Виктору Костану за рецензирование этого руководства. Спасибо Эйдзи Китамуре, Адди Османи и Марку Коэну, написавшим оригинальные статьи, на которых основана эта книга. Эйдзи написал полезный инструмент под названием Browser Storage Abuser , который был полезен при проверке текущего поведения. Это позволяет вам хранить как можно больше данных и видеть ограничения хранилища в вашем браузере. Спасибо Франсуа Бофорту, который покопался в Safari, чтобы выяснить ограничения на объем хранилища, и Томасу Штайнеру за добавление информации об исходной частной файловой системе, сегментах хранилища, SQLite Wasm и общем обновлении контента в 2024 году.

Изображение героя создано Гийомом Больдуком на Unsplash .