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

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

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

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

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

IndexedDB, OPFS и API Cache Storage поддерживаются в каждом современном браузере. Они асинхронны и не блокируют основной поток (но есть также синхронный вариант 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% от общего дискового пространства.
    • Если пользователь включил функцию «Удалять файлы cookie и данные сайтов при закрытии всех окон» в Chrome, квота хранилища значительно сокращается и составляет около 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 и API Cache, и позволяет рассчитать приблизительный оставшийся объем доступного хранилища.

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 была добавлена ​​новая функция, которая позволяет переопределять квоту хранения сайта в панели Storage. Эта функция дает вам возможность имитировать различные устройства и тестировать поведение ваших приложений в сценариях с низкой доступностью диска. Перейдите в Application , затем Storage , включите флажок Simulate custom storage quota и введите любое допустимое число для имитации квоты хранения.

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

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

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

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

Индексированная БД

Если источник превысил свою квоту, попытки записи в IndexedDB будут неудачными. Будет вызван обработчик транзакции onabort() , передающий событие. Событие будет включать DOMException в свойстве ошибки. Проверка 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 кэширования

Если источник превысил свою квоту, попытки записи в 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
  }
}

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

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

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

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

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

Начиная с iOS и iPadOS 13.4 и Safari 13.1 на macOS, существует семидневный лимит на все хранилища, доступные для записи скриптов, включая IndexedDB, регистрацию service worker и 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 Private File System, чтобы узнать подробности о том, как его использовать.

Заключение

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

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

Спасибо

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

Автор главного изображения — Гийом Больдюк с сайта Unsplash .