Переход с Ember-Data на Ember-Orbit

Вот набор советов и изменений, которые мне пришлось учесть при преобразовании большого проекта ember-data (ED) в ember-orbit (EO) и Orbit.js. Я надеюсь, что это поможет вам хотя бы оценить размер усилий, которые могут потребоваться, чтобы сделать то же самое в вашем собственном проекте. Наша мотивация заключалась в том, чтобы использовать почти все возможности, которые предоставляет Orbit (автономный кеш, журналы транзакций, стратегии фоновой синхронизации), но первым приоритетом было предоставление функции отмены / возврата в приложении с другим существующим поведением. На данный момент наша конверсия все еще продолжается, и я все еще учусь новому.

Я не собираюсь представлять Orbit, так как другие уже проделали достойную работу с этим. Если вы не знакомы с Orbit, вы можете начать с:

Обратите внимание: я пишу это на Ember-Orbit v0.16.8, а бета-версия 0.17 находится в стадии разработки - обязательно ознакомьтесь с примечаниями к выпуску (CHANGELOG.md) на GitHub, чтобы узнать о критических изменениях или исключениях.

Начиная

Моя стратегия была направлена ​​на то, чтобы сначала сохранить существующее поведение ember-data, с которого мы начали, прежде чем пытаться интегрировать дополнительные возможности Orbit. Подход, который я использовал (у нас много моделей), заключался в преобразовании одной или двух моделей в качестве доказательства концепции от начала до конца - у меня были оба ember-data и ember -orbit модели и магазины сосуществуют, пока я не достиг точки, когда отношения между моделями были слишком велики, чтобы управлять ими обоими.

  • Стратегии синхронизации: вы, вероятно, захотите начать с пессимистических стратегий синхронизации, чтобы максимально приблизиться к существующей логике данных ember (например, там, где вы раньше ждали обещания .save() на запись.) (На самом деле tchak работает над некоторыми заранее разработанными стратегиями, одна из которых, как я понимаю, должна быть довольно близка к пессимистическому поведению по умолчанию.) Вы можете начать с добавления вашего удаленного источника и три пессимистических стратегии, предложенные в todomvc-ember-orbit:
ember g data-source remote --from=@orbit/jsonapi
ember g data-strategy remote-store-sync
ember g data-strategy store-beforequery-remote-query
ember g data-strategy store-beforeupdate-remote-update
  • Сегменты. Я настоятельно рекомендую вам начать с отсутствия постоянных сегментов вообще - это избавит вас от некоторых проблем, когда вы начнете отладку. Когда вы быстро переносите код и модели данных и постоянно обновляетесь с помощью ember serve, последнее, что вам нужно, - это повторная попытка некоторых кешированных запросов со старыми моделями. Это слишком большой скачок для начала вашего рефакторинга.
  • Магазин. К счастью, в ember-orbit есть собственная реализация Store, которая добавляет некоторые более знакомые формы манипуляции, так что для многих простых вызовов вам нужно лишь немного настроить; они будут больше похожи на тлеющие данные, а не на обычную Орбиту. Если вы собираетесь использовать хранилища ED и EO одновременно, воспользуйтесь конфигурацией среды, чтобы установить для введенного ключа хранилища EO значение, отличное от «store» (см. EO README.md.)

Адаптеры и сериализаторы JSONAPI

  • Настройка: если вам нужно настроить свою модель или имена атрибутов, поскольку они становятся частью вызовов JSONAPI ajax, вместо добавления /adapters/application.js вы можете расширить JSONAPISerializer и назначить его параметру SerializerClass при создании экземпляра JSONAPISource в data-sources/remote.js. (Может отличаться на 0,17)
  • Инфлектор по умолчанию для ember-orbit не так хорошо плюрализирует (0,16) - у нас был ресурс на сервере под названием подходы, поэтому получение подходов не удалось. Вы можете ember install ember-inflector и использовать его pluralize() и singularize() в пользовательском сериализаторе вместо значения по умолчанию this.schema.pluralize(). Обратите внимание, настройка инфлектора была улучшена в v0.17.
  • Здесь вы также можете задать пространство имен URL (как вы это делали ранее в адаптере приложения).

Модели

  • Преобразование определений ED в EO довольно просто - иногда просто изменение операторов импорта.
  • Поддерживаемые типы атрибутов: напрямую поддерживаются только примитивные типы. Вы можете использовать массивы или объекты в качестве значений, но вы должны вручную убедиться, что вы преобразовываете значения, передавая полные клоны. Чтобы указать это, просто опустите свойство type в определении атрибута.
  • Значения атрибутов по умолчанию. Если вы привыкли определять атрибуты модели с помощью параметра { defaultValue }, вам понадобится другое решение. Я вроде как поддержал это, переопределив статический attributes геттер на Model, чтобы включить все мета (и, следовательно, options.defaultValue) в вычисляемое свойство attr. Затем я расширил Store, чтобы инициализировать реестр значений атрибутов по умолчанию для просмотра в течение addRecord. Очевидный недостаток здесь в том, что это работает только с store.addRecord - если вы пройдете чистую Орбиту через store.update(t => t..., вам не повезло. Возможно, вы могли бы проконсультироваться с реестром в адаптере ближе к металлу.
  • Ember-Data использует дашеризованные ссылки на названия моделей, в то время как ember-orbit использует верблюжий регистр.
  • Полиморфные отношения в ED определяются с помощью родительского класса и флага { polymorphic: true }. В обычном Orbit тип отношения указывается как массив типов (например ,@hasMany(['cat', ‘dog']) pets;). (Для этой поддержки у вас должна быть ember-orbit 0.16.7 или выше.)
  • В настоящее время он наивно просматривает каталог моделей данных для загрузки схемы модели. Если у вас есть модели, отличные от Orbit, вам придется переместить их в родственный каталог. По умолчанию, но настраиваемое имя каталога модели - /data-models, что может помочь вам постепенно переходить с сохранением /models для ED.
  • Сейчас самое важное, что нужно помнить, это то, что отношения, полученные с помощью геттера, больше не возвращают Promise - он возвращает только то, что находится в кеше, синхронно. Подробнее об этом позже…
  • Octane get / set: вы можете использовать геттеры с точечной нотацией и, если вы используете 0.17, сеттеры. Но я считаю, что явные вызовы (replaceAttribute, replaceRelatedRecord и т. Д.) Более понятны и полезны, особенно с пессимистическими стратегиями обработки данных, поскольку эти версии возвращают обещание, а set - нет.
  • Сохранение: удалите все свои .save()s. Это может занять некоторое время, особенно если у вас много того, что было у нас: Promise.all(anArrayOfAWholeBunchOfSaves) и save().then(). Один метод для групп изменений - использовать store.fork(), внести большую группу изменений, а затем store.merge() форк. Мне было указано, что во избежание утечки памяти не забывайте .destroy() использовать вилку, когда закончите с ней.

Создание, обновление, удаление и запросы

  • Создание записей: addRecord({ type: 'planet', …other attributes }), а не createRecord(‘planet’, { attributes }).save() ED. Теперь вам нужно будет принять взамен обещание, а не несохраненную (синхронную) запись.
  • Подглядывание: store.peekRecord(s) можно оставить прежним или заменить на store.cache.findRecords(s).
  • Удаление: model.destroyRecord() становится либо model.remove(), либо store.removeRecord(record), и оба возвращают обещание.
  • Обновить атрибуты: в ED: record.set('attribute', value) (или в Octane, record.attribute = value,) плюс record.save(), чтобы получить обещание. В EO, если вам нужно заблокировать его, используйте record.replaceAttribute('attribute', value), поскольку он возвращает Promise. record.set('attribute', value) можно использовать и вызывает replaceAttribute под капотом, но не возвращает обещание. Если вы хотите сгруппировать несколько в транзакцию, посмотритеrecord.replaceAttributes(...). Напомним также, что если вы используете атрибуты со значениями Array или Object, вы должны передать новые копии.
  • Связанные с извлечением: solarSystem.get(planets) (или solarSystem.planets) и solarSystem.getRelatedRecord/getRelatedRecords синхронны на орбите тлеющего угля. Они проверяют только кеш магазина на наличие связанных записей, что может означать, что вы сразу же получите обратно идентификационные данные ({ type, id }), хотя это будет оперативный запрос, который загружает их в фоновом режиме. Если вам нужно, чтобы это было по-настоящему, вы хотите дождаться обещания. Итак, await store.liveQuery(q => q.findRelatedRecords(planet, 'moons') возвращает вам обещание и массив записей в реальном времени (или store.query тоже). Мне было рекомендовано защитить от утечек памяти с помощью liveQuery.unsubscribe(). В документации к версии 0.17 следует упомянуть об этом больше.
  • Связанное с обновлением: planetarySystem.set('star', sunRecord) становится planetarySystem.replaceRelatedRecord('star', sunRecord) или даже store.update(t => t.replaceRelatedRecord(planetarySystem.identity, 'star', sunRecord.identity)).
  • Отправка полезных данных: если вы также получаете данные за пределами обычного конвейера ED, например через веб-сокеты, возможно, вы использовали store.pushPayload(). Чак поиграл здесь с примером: https://github.com/orbitjs/orbit/pull/640. До сих пор я делал это, (1) десериализуя ресурс, (2) создавая массив преобразований с помощью построителя (this.source.transformBuilder), затем (3) исправляя кеш: this.source.cache.patch(transforms).
  • Два API: о чем следует помнить - когда вы используете построитель преобразований (например, store.update(t => t.replaceRelatedRecord(..., а не интерфейсы модели, построитель находится на земле Orbit.js с точки зрения параметров API. Вы должны быть будьте осторожны, чтобы не передавать модели Ember-Orbit в те функции, которые ожидают идентификаторов ({ type, id }). Я обнаружил, что легко повторить эту ошибку несколько раз. Если вы обнаружите исключение «Превышен максимальный размер стека вызовов», это может быть причиной.

Тесты

  • Если ваши приемочные или интеграционные тесты полагались на хранилище данных ember, то, конечно, здесь потребуется некоторая работа. Замечу, что есть помощник по тестированию waitForSource(store), с которым вы можете await.

Спасибо команде Orbit.js за помощь в ответах на некоторые вопросы, которые привели к этому набору советов!

[Обновлено: 27 сентября 2020 г. с учетом отзывов от tchak и dgeb]

Фото: https://flic.kr/p/bxduiW