Redux + Firebase + localStorage для поддержки в автономном режиме

Я провожу много времени, защищая Firebase. Я был моногамен Firebase с 2014 года, когда я поцеловал последний кусок кода MySQL на прощание и избавился от него.

Я снова и снова получаю несколько вопросов о Firebase

  1. Как вы ищите коллекции Firebase?
  2. Есть ли у Firebase офлайн-поддержка Интернета?

Мой последний проект HiiTClock.com заставил меня сразу заняться обеими этими проблемами.

Что такое HiiT Clock?

HiiT означает интервальная тренировка высокой интенсивности. Это популярный способ стать быстрым и сильным, который используется во многих видах спорта, таких как бег, велоспорт, кроссфит и футбол. Идея состоит в том, что вы выполняете очень интенсивное движение, например, бег на 100 метров или толкаете санки с утяжелением. Затем вы отдыхаете определенное время… затем повторяете снова, снова и снова. Регулируя интервалы работы / отдыха, вы можете добиться максимальной производительности от своего тела за короткий промежуток времени.

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

Я много тренируюсь в стиле HiiT, особенно когда тренируюсь по кроссфиту. Мне не удалось найти простого в использовании, но мощного таймера, который помогал бы мне не отставать от тренировок. В тренажерных залах CrossFit традиционно используются доска и одни часы, а спортсмены смотрят на часы и доску, мысленно подсчитывая их, чтобы определить следующее движение. За эти годы я использовал кучу таймеров, но все они так или иначе беспокоили меня.

HiiT Clock позволяет пользователям запрограммировать всю свою схему HiiT в приложении и сохранить ее на будущее. HiiT Clock также позволяет транслировать таймеры на телевизор с помощью Chromecast, Apple AirPlay или зеркальной веб-страницы. Он также имеет очереди аудио, которые особенно полезны при работе с наушниками.

Стек часов HiiT

HiiT Clock использует множество технологий. Вот несколько.

Ключевым нововведением HiiT Clock является то, что это прогрессивное веб-приложение или PWA. PWA строятся так, чтобы в первую очередь работать в автономном режиме, используя serviceWorker для кеширования всей страницы. Они также созданы для сохранения на главном экране телефона и работают точно так же, как родное приложение на любом телефоне Android. Они также могут работать на iOS, но iOS имеет меньшую поддержку функций PWA, поэтому приложения PWA в конечном итоге работают больше как обычные веб-приложения при использовании в Safari. Но они по-прежнему работают нормально, поэтому они «прогрессивные».

Если у вас телефон или планшет Android, попробуйте сохранить HiiT Clock на главном экране. При сохранении на главном экране PWA могут выглядеть на 100% родными. Единственное, что намекает на то, что это веб-приложения, - это то, что все они имеют функцию "потянуть для обновления", как и любая вкладка Android Chrome, и когда вы пытаетесь удалить приложение, вы получаете параметр удалить, но не удалить. Вы можете перевести телефон в режим полета, обновить страницу, и он должен продолжать работать без сбоев.

Не пытайтесь выполнять поиск в Firebase. Вместо этого используйте Algolia.

можно составить базовый поиск Firebase. Обычно это связано с логикой на стороне клиента и обычно включает загрузку целых коллекций.

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

Мы все хотели бы, чтобы Google уже купил Algolia и запустил свой невероятный поиск прямо в Firebase, но до того маловероятного, но ожидаемого дня у меня есть модуль NPM, который может помочь.

npm install --save quiver-firebase-search

Мой модуль называется firebase-search. Кто-то уже зарегистрировал firebase-search на npm, поэтому вам нужно будет установить quiver-firebase-search ... но как только вы его установили, это будет просто firebase-search.

Firebase Search принимает два аргумента: ссылку на коллекцию Firebase и объект конфигурации. Firebase Search будет синхронизировать ваш индекс Algolia или Elasticsearch с вашей коллекцией Firebase ref. Задача решена.

Используйте Redux + localStorage для офлайн

Я слышал, что Firebase имеет отличную офлайн-поддержку для iOS и Android. Но мне все равно. Я разрабатываю прогрессивное веб приложение. Мне нужен автономный режим для JavaScript, и Firebase не дает мне этого сразу.

Войдите в Redux. Большинство разработчиков думают, что Redux создан для React. Ложь! Redux построен для JavaScript. Полимер - это JavaScript. Я использую Redux с фантастическим элементом ‹polymer-redux›. Всякий раз, когда вы думаете про себя: Хотелось бы, чтобы был лучший способ управлять состоянием моего приложения…, попробуйте Redux. Вы не разочаруетесь.

Одно из критических замечаний к Polymer заключается в том, что он допускает двустороннюю привязку данных. Ну сюрприз! Полимер также допускает одностороннюю привязку данных. Redux - это односторонняя привязка данных, поэтому они идеально подходят.

Шшш!

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

Redux + localStorage == Автономная поддержка

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

var store = Redux.createStore(function (state, action) {
  if (action.type == '@@redux/INIT') {
    try {
      state =JSON.parse(localStorage.getItem('QuiverStoreState'));
    } catch (err) {
      console.log('localStorage error', err);
    }
   }
   if (!state) { // Handle empty localStorage + undefined
      state = {//Some default state}
   }
  // The rest of your reducer
});
store.subscribe(function () {
 localStorage.setItem(‘QuiverStoreState’, JSON.stringify(store.getState()));
 });

Redux + Firebase == Постоянство в сети

Вот тут-то и бывает сложно. Когда ваше приложение полностью автономно и вас устраивают Redux и localStorage, пора начать синхронизацию с Firebase.

Этот шаг полностью зависит от приложения.

HiiT Clock позволяет пользователям создавать свои собственные интервальные таймеры, которые затем можно воспроизводить на экране с визуальным графиком обратного отсчета. Я решил синхронизировать с Firebase только коллекции таймеров пользователей. Остальная часть модели данных HiiT Clock является самодостаточной и удобной для работы в автономном режиме… она может быть взорвана обновлением кеша, и меня это не волнует. Но эти таймеры очень важны.

Чтобы добиться синхронизации Redux-to-Firebase, я подписался на свой магазин Redux и, когда меняются таймеры, я пытаюсь синхронизировать эти данные с Firebase. Если клиент не в сети, синхронизация не удалась, и я попробую еще раз позже.

Чтобы добиться синхронизации Firebase-to-Redux, я прослушиваю событие value в моей ссылке Firebase и сравниваю все входящие состояния Firebase с моим локальным состоянием Redux. Если я определю, что мое состояние Firebase было обновлено совсем недавно, я перезапишу свое локальное состояние Redux.

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

state.updated = Date.now()

Поэтому, если я вижу локальный штат с более поздним значением state.updated, чем в Firebase, я заменяю Firebase своим локальным состоянием. Если состояние Firebase более новое, я перезаписываю свое локальное состояние Redux.

Синхронизация становится волосатой

Сторона Redux + localStorage в этом уравнении была простой. На стороне Redux + Firebase не было.

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

Если бы моя синхронизация Redux-to-Firebase-to-Redux была более сложной, у меня были бы настоящие проблемы. Я подозреваю, что именно поэтому Firebase не пыталась реализовать офлайн-решение для Интернета. Решить, какое хранилище данных должно выиграть, не всегда просто. В следующем приложении у меня все получится. Сначала я сделаю модель данных, а потом пользовательский интерфейс. Я начну с Redux и Firebase и начну синхронизировать эти данные с самого начала.

Что вы думаете?

Я сумасшедший, пытаясь использовать Redux с Firebase?

Похоже ли это на полезную архитектуру для ваших приложений, или я просто нашел нишевый вариант использования?

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