Хранение данных в веб-браузере
Существует множество разных механизмов хранения данных в браузере. Какой из них лучше всего подходит для ваших потребностей?
Когда пользователь находится в пути, его интернет-соединение может быть неустойчивым или вовсе отсутствовать; именно поэтому прогрессивные веб-приложения часто поддерживают офлайн-работу и оптимизированы для устойчивой производительности. Даже в идеальной беспроводной среде разумное использование кеширования и других методов хранения данных может существенно повысить комфорт пользователя. Существует несколько способов кеширования как статических ресурсов приложения (HTML, JavaScript, CSS, изображений и т. д.), так и контента (пользовательских данных, новостных статей и т. д.). Но какое решение лучше? Какой объем данных можно хранить? Как предотвратить их очищение?
Какой способ следует использовать? #
Вот общая рекомендация по хранению ресурсов:
- Для сетевых ресурсов, необходимых для загрузки приложения и файлового контента, используйте Cache Storage API (часть API сервис-воркеров).
- Для других видов данных используйте IndexedDB (с библиотекой-оберткой на основе Promises).
IndexedDB и Cache Storage API поддерживаются всеми современными браузерами. Оба API являются асинхронными и не блокируют основной поток. Они доступны из объекта window
, веб-воркеров и сервисных воркеров, благодаря чему их легко можно использовать в любом месте вашего кода.
Как насчет других механизмов хранения? #
Существует ряд других механизмов хранения, доступных в браузере, но они имеют ограниченную область применения и могут вызывать серьезные проблемы с производительностью.
API SessionStorage имеет привязку к конкретной вкладке и хранит данные только в течение времени ее существования. Его можно использовать для хранения небольших объемов информации о конкретном сеансе, такой как ключ IndexedDB. При его использовании будьте осторожны, поскольку он работает в синхронном режиме и блокирует основной поток. Он поддерживает хранение не больше 5 МБ данных и может хранить только строки. В связи с привязкой к конкретной вкладке его нельзя использовать в веб-воркерах и сервис-воркерах.
API LocalStorage следует избегать, поскольку он работает в синхронном режиме и блокирует основной поток. Он поддерживает хранение не больше 5 МБ данных и может хранить только строки. LocalStorage нельзя использовать в веб-воркерах и сервис-воркерах.
Файлы cookie — полезный инструмент, но их не следует использовать в качестве хранилища. Поскольку они пересылаются на сервер с каждым HTTP-запросом, хранить в них следует только очень небольшие объемы данных, иначе размер каждого веб-запроса существенно увеличится. Они работают в синхронном режиме и недоступны в веб-воркерах. Как и LocalStorage или SessionStorage, файлы cookie могут хранить только строки.
File System API и FileWriter API предоставляют методы для чтения и записи файлов в пределах изолированной файловой системы. Хотя они работают в асинхронном режиме, использовать их не рекомендуется, поскольку они доступны только в браузерах на основе Chromium.
File System Access API был разработан, чтобы упростить для пользователей чтение и редактирование файлов, расположенных в локальной файловой системе. Прежде чем страница сможет читать или записывать локальные файлы, пользователь должен предоставить ей разрешение, которое не сохраняется между сеансами.
WebSQL не рекомендуется к использованию, а существующие сайты на его основе должны быть переведены на IndexedDB. Поддержка WebSQL была удалена практически из всех основных браузеров. Консорциум W3C прекратил поддержку спецификации Web SQL в 2010 году, и дальнейших обновлений не планируется.
Application Cache не рекомендуется к использованию, а существующие сайты на его основе должны быть переведены на сервис-воркеры и Cache API. Этот API устарел, и в будущем его поддержка в браузерах будет прекращена.
Сколько данных можно хранить? #
Много: как минимум пару сотен мегабайт, а в теории — сотни гигабайт или больше. Реализации в браузерах различаются, однако объем, доступный для хранения данных, обычно зависит от объема памяти устройства.
- Chrome позволяет браузеру использовать до 80% всего дискового пространства. Один источник может использовать до 60% от общего дискового пространства. Чтобы определить максимально возможную квоту, можно воспользоваться StorageManager API. В других браузерах на основе Chromium ограничения могут быть более мягкими. Подробнее о реализации в Chrome см. здесь: PR #3896.
- Internet Explorer 10 и более поздних версий позволяет хранить до 250 МБ и будет запрашивать разрешение пользователя, если требуется сохранить более 10 МБ.
- Firefox позволяет браузеру использовать до 50% свободного дискового пространства. Домены в пределах одной группы eTLD+1 (например,
example.com
,www.example.com
иfoo.bar.example.com
) могут использовать до 2 ГБ. Чтобы узнать оставшийся объем доступного места, используйте StorageManager API. - Safari (как для ПК, так и для мобильных устройств) позволяет использовать около 1 ГБ. По достижении лимита Safari предлагает пользователю увеличить его с шагом в 200 МБ. Мне не удалось найти официальной документации, описывающей это поведение.
- Если из мобильного Safari добавить PWA-приложение на экран «Домой», для него создается отдельный контейнер с данными, никак не связанный с мобильным Safari. Когда в контейнере установленного PWA-приложения заканчивается место, запросить его увеличение, по всей видимости, нельзя.
В старых версиях браузеров, когда сайт превышал допустимый объем сохраненных данных, пользователю предлагалось предоставить разрешение на увеличение объема. Например, если источник использовал более 50 МБ, браузер предлагал пользователю увеличить объем до 100 МБ, а затем вновь отображал запрос через каждые последующие 50 МБ.
Сегодня большинство браузеров позволяют сайту использовать все пространство в пределах квоты, не спрашивая разрешения пользователя. Исключением является Safari, который по достижении 750 МБ запрашивает разрешение на то, чтобы расширить хранилище до 1,1 ГБ. Если источник попытается выйти за пределы квоты, дальнейшие попытки записи данных будут завершаться ошибкой.
Как проверить доступный объем для хранения данных? #
Во многих браузерах можно использовать StorageManager API для определения объема хранилища, доступного источнику, а также объема уже записанных данных. API позволяет узнать общее количество байт, используемых 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.`);
}
StorageManager API пока реализован не во всех браузерах, поэтому перед использованием необходимо проверить его наличие. Даже если он доступен, вам по-прежнему необходимо будет перехватывать ошибки превышения квоты (см. ниже). В некоторых случаях доступная квота может превышать фактический объем доступного пространства.
Инспектирование #
Во время разработки вы можете отслеживать состояние всех хранилищ и с легкостью очищать все сохраненные данные, используя DevTools браузера.
В Chrome 88 была добавлена новая функция: теперь на панели «Хранилище» можно изменить размер пространства, отведенного для хранилища сайта. При помощи этой функции можно имитировать различные устройства и тестировать поведение приложений в условиях недостатка места на диске. Перейдите в раздел Приложение, затем Хранилище, установите флажок Имитировать настраиваемое место в хранилище и укажите размер квоты хранилища, который требуется имитировать.

Во время работы над этой статьей я написал простой инструмент, позволяющий быстро заполнять хранилище настолько, насколько это возможно. Он позволяет быстро и легко экспериментировать с различными механизмами хранения данных и смотреть, что происходит при исчерпании квоты.
Как реагировать на превышение квоты? #
Что следует делать, когда происходит превышение квоты? Прежде всего, всегда перехватывайте и обрабатывайте ошибки записи, будь то QuotaExceededError
или любая другая ошибка. Затем, в зависимости от принципов работы вашего приложения, следует выбрать способ их обработки. Например, можно удалять контент, к которому давно не обращались, или данные, которые занимают больше всего места, либо предлагать пользователю самостоятельно выбрать данные для удаления.
Как IndexedDB, так и Cache API при превышении доступной квоты генерируют ошибку DOMError
с именем QuotaExceededError
.
IndexedDB #
Если источник превысил свою квоту, попытка записи в IndexedDB завершится неудачно. Будет вызван метод транзакции onabort()
с событием в качестве аргумента. Событие будет содержать в свойстве error ошибку 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
}
};
Cache API #
Если источник превысил свою квоту, попытки записи с использованием Cache API будут отклонены с ошибкой DOMException
QuotaExceededError
.
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 начинают очистку данных, когда у браузера заканчивается место. Сначала браузер удаляет данные, относящиеся к наименее востребованному (на основании давности использования) источнику, затем переходит к следующему — и так до тех пор, пока нехватка места не будет устранена.
- Internet Explorer 10+ не очищает данные, но при этом не дает источнику записывать данные сверх лимита.
- Firefox начинают очистку данных, когда заканчивается свободное место на диске. Сначала браузер удаляет данные, относящиеся к наименее востребованному (на основании давности использования) источнику, затем переходит к следующему — и так до тех пор, пока нехватка места не будет устранена.
- Safari раньше не производил очистку данных, но недавно в нем был реализован новый семидневный лимит на хранение всех записываемых данных (см. ниже).
Начиная с iOS и iPadOS 13.4, а также Safari 13.1 на macOS существует семидневный лимит на хранение всех данных, записываемых скриптами, включая IndexDB, регистрацию сервис-воркеров и Cache API. Это значит, что Safari будет удалять контент из кеша, если пользователь в течение семи дней использования Safari не взаимодействовал с соответствующим сайтом. Эта политика очистки не распространяется на установленные PWA, добавленные на экран «Домой». Подробности см. в статье Full Third-Party Cookie Blocking and More в блоге WebKit.
Бонус: зачем использовать обертку для IndexedDB #
IndexedDB — это низкоуровневый API, требующий сложной настройки перед использованием и поэтому крайне неудобный для хранения простых данных. В отличие от большинства современных API, основанных на Promises, он использует события. Обертки, основанные на Promises, такие как idb для IndexedDB, скрывают некоторые его мощные возможности, но, что более важно, скрывают доступные в IndexedDB сложные внутренние механизмы, такие как транзакции или версионирование схемы.
Заключение #
Прошли времена, когда сайты имели ограниченный объем хранилища и для его расширения приходилось запрашивать разрешение пользователя. Сайты могут хранить практически любые ресурсы и данные, которые требуются им для работы. При помощи StorageManager API можно узнать, какой объем доступен вашему сайту и сколько он уже израсходовал. А при помощи постоянного хранилища вы сможете защитить данные от очистки, если пользователь сам не удалит их.
Дополнительные ресурсы #
Благодарности #
Особая благодарность Джарриду Гудману, Филу Уолтону, Эйдзи Китамуре, Дэниелу Мерфи, Дарвину Хуангу, Джошу Беллу, Марейну Круиссельбринку и Виктору Костану за рецензирование этой статьи. Спасибо Эйдзи Китамуре, Адди Османи и Марку Коэну за написание оригинальных статей, на которых основана эта статья. Эйдзи создал полезный инструмент под названием Browser Storage Abuser, который пригодился для проверки текущего поведения. Он позволяет сохранить максимально допустимый объем данных и таким образом узнать лимиты хранилища в вашем браузере. Спасибо Франсуа Бофору за то, что он подробно изучил Safari и узнал его лимиты на хранение данных.
Автор изображения в шапке — Гийом Болдюк с Unsplash.