💾🌐 6 главных технологий для хранения данных в браузере

Ведущий инженер RxDB провел комплексный анализ и сравнение шести основных технологий хранения данных в браузере – от классических Cookies до современных WASM SQLite, оценивая их функциональность, производительность и потенциал для оптимизации.

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

Ведущий инженер RxDB сравнил возможности и производительность браузерных хранилищ – от всем известных Cookies и LocalStorage до относительно новых технологий OPFS и WASM SQLite.

Cookies

На ранних этапах развития интернета единственным способом хранения данных на клиенте были cookies. Они представляют собой маленькие пары «ключ-значение», которые подходят для управления сессиями, персонализации и отслеживания активности. Куки хранятся не только на клиенте, но и передаются на сервер с каждым HTTP-запросом. Хотя объем данных, который можно хранить в cookies, очень ограничен, их производительность остается важной для работы современных сайтов. Сейчас куки развиваются, в частности, в Chromium добавляется версия с поддержкой общей памяти и асинхронный API CookieStore.

LocalStorage и SessionStorage

LocalStorage, предложенный в 2009 году как часть спецификации WebStorage, позволяет хранить пары «ключ-значение» в браузере. Он прост в использовании и содержит только основные методы: setItem, getItem, removeItem и clear. Этот инструмент подходит для хранения небольших данных, которые должны сохраняться между сессиями – его объем ограничен 5 Мб. LocalStorage не поддерживает асинхронные операции, из-за чего может блокировать интерфейс во время выполнения продолжительных задач. SessionStorage работает аналогично, но его данные удаляются при закрытии вкладки или окна браузера.

IndexedDB

IndexedDB был представлен как API для хранения структурированных данных в JSON-формате в 2015 году. Это более мощный инструмент, чем LocalStorage, – позволяет хранить большие объемы данных, использовать индексы и выполнять асинхронные операции. Однако, он не поддерживает сложные запросы и предназначен для использования в качестве низкоуровневого слоя, на основе которого могут быть построены другие библиотеки. С выходом IndexedDB 2.0 производительность увеличилась за счет метода getAll(), позволяющего быстро получать большие объемы данных. В разработке находится IndexedDB 3.0, который будет включать асинхронные вызовы на основе промисов, что улучшит совместимость с async/await в JavaScript.

OPFS

OPFS (Origin Private File System) – это относительно новый API, который позволяет веб-приложениям хранить большие файлы прямо в браузере. Он предназначен для приложений, работающих с большим объемом данных, которым нужно читать и записывать бинарные данные в симулированной файловой системе. OPFS можно использовать двумя способами:

  1. Асинхронно в основном потоке.
  2. В WebWorker с использованием метода createSyncAccessHandle(), который обеспечивает более быструю, асинхронную обработку.

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

WASM SQLite

WebAssembly (Wasm) – это бинарный формат, позволяющий запускать высокопроизводительный код в браузере. WebAssembly был добавлен в популярные браузеры в 2017 году, что открыло новые возможности для запуска сложного кода прямо на клиенте. Библиотеки на нативных языках можно перекомпилировать в WebAssembly и запускать в браузере с минимальными изменениями. Обычно код на WebAssembly работает быстрее, чем на JavaScript, но примерно на 10% медленнее, чем в нативном окружении.

СУБД SQLite, скомпилированная в WebAssembly, стала популярным решением для встраивания базы данных в браузер. Скомпилированный код SQLite в формате WASM занимает около 938,9 Кб, и загружается при первом запуске страницы. WebAssembly не имеет прямого доступа к постоянному хранилищу в браузере, поэтому для передачи данных между WASM и основным потоком используются адаптеры виртуальной файловой системы (VFS), которые обеспечивают взаимодействие между SQLite и браузерными API для хранения данных.

👨‍💻🎨 Библиотека фронтендера
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека фронтендера»

Сравнение функциональности Cookies, LocalStorage, SessionStorage, IndexedDB, OPFS и WASM SQLite

Хранение сложных JSON-документов

Часто нужно сохранять в браузере не только простые строки и числа, но и сложные JSON-объекты.

  • IndexedDB позволяет работать с JSON-объектами напрямую.
  • SQLite в WebAssembly (начиная с версии 3.38.0) поддерживает хранение JSON в текстовых полях, а также позволяет выполнять глубокие запросы и использовать атрибуты JSON в качестве индексов.
  • LocalStorage, OPFS и Cookies могут хранить только строки или бинарные данные, поэтому JSON-объекты нужно предварительно конвертировать в строки с помощью JSON.stringify(), что может усложнить работу и замедлить выполнение запросов.

Поддержка нескольких вкладок

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

addEventListener("storage", (event) => {});

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

Поддержка индексации

Индексация позволяет быстро искать данные по заданным критериям. IndexedDB и SQLite в WebAssembly поддерживают индексацию по умолчанию. Например, в IndexedDB можно выбрать товары по диапазону цен:

В других API индексация недоступна, a построение собственных индексов требует много усилий.

Поддержка WebWorker

Для выполнения продолжительных операций можно использовать WebWorker, чтобы избежать блокировки основного потока JavaScript и улучшить отзывчивость интерфейса. LocalStorage и Cookies не могут использоваться в WebWorker из-за ограничений безопасности, тогда как IndexedDB, OPFS и SQLite в WebAssembly могут выполняться внутри WebWorker. В OPFS быстрый метод createSyncAccessHandle доступен только в WebWorker, чтобы избежать блокировки основного потока.

Ограничения по объему хранения

Cookies ограничены примерно 4 Кб данных. Это связано с тем, что cookies отправляются на сервер с каждым HTTP-запросом. LocalStorage имеет ограничения, которые зависят от браузера (обычно от 4 до 10 МБ на домен):

  • Chrome/Chromium/Edge – 5 Мб
  • Firefox – 10 Мб
  • Safari – 4-5 Мб

IndexedDB и OPFS зависят только от доступного дискового пространства на устройстве пользователя и могут хранить гигабайты данных.

Сравнение производительности различных методов хранения данных в браузере

Время инициализации

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

  • Для методов LocalStorage и Cookies инициализация не требуется – они готовы к использованию сразу.
  • IndexedDB требует открытия базы данных и создания хранилища, а WASM SQLite – загрузки и обработки файла WebAssembly.
  • OPFS также нуждается в загрузке файла Worker и инициализации виртуальной файловой системы.

Сравнение:

  • IndexedDB инициализируется примерно за 46 мс;
  • OPFS в основном потоке – 23 мс;
  • OPFS в WebWorker – 26,8 мс;
  • WASM SQLite (память) – 504 мс;
  • WASM SQLite (IndexedDB) – 535 мс.

Задержка для небольших операций записи

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

Сравнение:

  • LocalStorage – самая низкая задержка, всего 0,017 мс на запись;
  • Cookies – 0,058 мс;
  • IndexedDB – около 0,17 мс;
  • OPFS (основной поток) – 1,46 мс;
  • OPFS (WebWorker) – 1,54 мс;
  • WASM SQLite (память) – 0,17 мс,;
  • WASM SQLite (IndexedDB) – 3,17 мс.

Задержка для небольших операций чтения

LocalStorage очень быстро выполняет операции чтения – всего за 0.0052 мс. Другие технологии выполняют чтение примерно с такой же скоростью, что и запись:

Массовые записи

Оценка времени, необходимого для записи 200 документов:

Массовые чтения

Оценка времени для чтения 100 документов:

Выводы о производительности

  • LocalStorage очень быстрый, но блокирует основной поток, что делает его непригодным для больших операций.
  • OPFS показывает лучшую производительность в WebWorker с использованием метода createSyncAccessHandle(), чем в основном потоке.
  • SQLite WASM может быть быстрой, но требует загрузки и инициализации, что занимает около полсекунды.
  • IndexedDB недавно начал поддерживать (пока только в Chrome) хранилища, которые позволяют организовать данные в логические группы или контейнеры. Это позволяет более эффективно управлять и структурировать большие объемы данных.

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

Ускорение IndexedDB

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

Оптимизация OPFS для хранения нескольких документов

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

Комбинирование технологий

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

Отображение файла в памяти

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

Сжатие данных перед сохранением

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

Шардинг через несколько WebWorker-ов

Разделение работы между несколькими WebWorker-ами (шардинг) позволяет задействовать максимальные ресурсы устройства пользователя и улучшить общую производительность.

Возможное улучшение WebAssembly

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

Ускорение передачи данных между потоками

Передача данных между основным потоком и WebWorker-ом с помощью postMessage() может быть медленной. Если браузеры улучшат эту функцию, это позволит ускорить обмен данными и повысит общую производительность работы с хранилищами данных.

***

Хотите стать Frontend-разработчиком?

Курс Frontend Basic от Proglib Academy предлагает:

  • Комплексное обучение: от HTML и CSS до React.js
  • 26 видеоуроков и 28 практических заданий
  • Опытные преподаватели из индустрии
  • Возможность создать 4 проекта для портфолио
  • Гибкий формат: 2 месяца самостоятельного обучения

Ключевые навыки, которые вы освоите:

  • Адаптивная верстка
  • JavaScript для интерфейсов и работы с backend
  • Основы Git для командной работы

Бонус: опция персонального менторства для углубленного изучения.

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ