Переход с Ember-Data на Ember-Orbit
Вот набор советов и изменений, которые мне пришлось учесть при преобразовании большого проекта ember-data (ED) в ember-orbit (EO) и Orbit.js. Я надеюсь, что это поможет вам хотя бы оценить размер усилий, которые могут потребоваться, чтобы сделать то же самое в вашем собственном проекте. Наша мотивация заключалась в том, чтобы использовать почти все возможности, которые предоставляет Orbit (автономный кеш, журналы транзакций, стратегии фоновой синхронизации), но первым приоритетом было предоставление функции отмены / возврата в приложении с другим существующим поведением. На данный момент наша конверсия все еще продолжается, и я все еще учусь новому.
Я не собираюсь представлять Orbit, так как другие уже проделали достойную работу с этим. Если вы не знакомы с Orbit, вы можете начать с:
- Сайт Orbit JS: https://orbitjs.com/
- Презентация Дэна Ember Conf 2018 о будущем данных в Ember (перейдите к отметке 28:05)
- Презентация Cibernox Emberfest, октябрь 2019
- Ember-Orbit: https://github.com/orbitjs/ember-orbit и https://github.com/orbitjs/todomvc-ember-orbit
- Https://codingitwrong.com/2018/05/10/ember-orbit.html
Обратите внимание: я пишу это на 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