Использование IndexedDB в качестве кеша в браузере. Я делаю это правильно? - PullRequest
1 голос
/ 21 марта 2019

На моем сайте:

  • У пользователей есть много действий
  • Каждое действие имеет данные encoded_polyline
  • Я отображаю эти encoded_polylines на карте

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


Вот высокоуровневое описание всего процесса:

  • Выясните, что существует на сервере
  • Удалите все, что присутствует в IndexedDB, но отсутствует на сервере
  • Выясните, что существует в IndexedDB
  • Запрос только данных, отсутствующих в IndexedDB
  • Сохранение новых данных в IndexedDB
  • Запрос всех данных из IndexedDB

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


Вот версия того, что я решил сделать на английском языке:

  • Соберите все идентификаторы активности этого пользователя с сервера
  • Удалите все в IndexedDB, чего там быть не должно (вещи, удаленные с сайта, которые все еще могут существовать в IndexedDB)
  • Соберите все данные этого пользователяИдентификаторы активности из IndexedDB
  • Отфильтруйте все, что присутствует в IndexedDB и на сервере
  • Если нет новых encoded_polylines для извлечения, тогда putItemsFromIndexeddbOnMap (описано ниже)
  • Если естьявляются новыми codeded_polylines для извлечения: получить их с сервера, затем сохранить их в IndexedDB, затем putItemsFromIndexeddbOnMap

Для putItemsFromIndexeddbOnMap:

  • Получить все encoded_polylines этого пользователя изIndexedDB
  • Вставить эти данные в массив
  • Показать этот массив данных на карте

Воткод JavaScript, который делает то, что я объяснил выше (с некоторым ERB, потому что этот JavaScript встроен в представление Rails):

var db = new Dexie("db_name");
db.version(1).stores({ activities: "id,user_id" });
db.open();

// get this user's activity IDs from the server
fetch('/users/' + <%= @user.id %> + '/activity_ids.json', { credentials: 'same-origin' }
).then(response => { return response.json() }
).then(activityIdsJson => {
  // remove items from IndexedDB for this user that are not in activityIdsJson
  // this keeps data that was deleted in the site from sticking around in IndexedDB
  db.activities
    .where('id')
    .noneOf(activityIdsJson)
    .and(function(item) { return item.user_id === <%= @user.id %> })
    .keys()
    .then(removeActivityIds => {
      db.activities.bulkDelete(removeActivityIds);
    });

  // get this user's existing activity IDs out of IndexedDB
  db.activities.where({user_id: <%= @user.id %>}).primaryKeys(function(primaryKeys) {
    // filter out the activity IDs that are already in IndexedDB
    var neededIds = activityIdsJson.filter((id) => !primaryKeys.includes(id));

    if(Array.isArray(neededIds) && neededIds.length === 0) {
      // we do not need to request any new data so query IndexedDB directly
      putItemsFromIndexeddbOnMap();
    } else if(Array.isArray(neededIds)) {
      if(neededIds.equals(activityIdsJson)) {
        // we need all data so do not pass the `only` param
        neededIds = [];
      }

      // get new data (encoded_polylines for display on the map)
      fetch('/users/' + <%= @user.id %> + '/encoded_polylines.json?only=' + neededIds, { credentials: 'same-origin' }
      ).then(response => { return response.json() }
      ).then(newEncodedPolylinesJson => {
        // store the new encoded_polylines in IndexedDB
        db.activities.bulkPut(newEncodedPolylinesJson).then(_unused => {
          // pull all encoded_polylines out of IndexedDB
          putItemsFromIndexeddbOnMap();
        });
      });
    }
  });
});

function putItemsFromIndexeddbOnMap() {
  var featureCollection = [];

  db.activities.where({user_id: <%= @user.id %>}).each(activity => {
    featureCollection.push({
      type: 'Feature',
      geometry: polyline.toGeoJSON(activity['encoded_polyline'])
    });
  }).then(function() {
    // if there are any polylines, add them to the map
    if(featureCollection.length > 0) {
      if(map.isStyleLoaded()) {
        // map has fully loaded so add polylines to the map
        addPolylineLayer(featureCollection);
      } else {
        // map is still loading, so wait for that to complete
        map.on('style.load', addPolylineLayer(featureCollection));
      }
    }
  }).catch(error => {
    console.error(error.stack || error);
  });
}

function addPolylineLayer(data) {
  map.addSource('polylineCollection', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: data
    }
  });
  map.addLayer({
    id: 'polylineCollection',
    type: 'line',
    source: 'polylineCollection'
  });
}

... Правильно ли я делаю?

...