3+ года Ember, 6 месяцев React

Dan Steshenko
devSchacht
Published in
12 min readNov 1, 2017

--

Перевод статьи Filippos Vasilakis: 3+ years of Ember, 6 months of React.

TL;DR: Фреймворк это всегда хорошо, особенно для разработки быстрорастущего продукта или когда нужно поддерживать код в непостоянной команде

Я помню свой первый опыт работы с Ember: это было немногим больше трёх лет назад, когда Ember находился в версии 1.5.7. Требованием клиента было «чтобы работало оффлайн и было похоже на магазин на Rails», поэтому казалось правильным выбрать фреймворк, следующий соглашениям по конфигурации — основной философии Rails. Тогда я в первый раз увидел ember-cli (интерфейс командной строки для Ember), который был нужен для помощи при настройке приложения и имел генераторы файлов, вдохновленные Rails, типа ember generate route post. Мне никогда не нравились генераторы в Rails, поэтому и здесь я их не использовал, но все же ember-cli позволял сделать первоначальную настройку с легкостью: без ember-cli придется подключать и настраивать скрипты и всё остальное самостоятельно, что напоминает мне фронтенд-разработку прошлого десятилетия. Я помню, как сомневался, сделал ли правильный выбор в пользу ember-cli для работы с Ember.

После прочтения всей документации «фреймворка для создания амбициозных веб-приложений» я сел и стал создавать мои первые роуты и шаблоны, следуя спецификации дизайна. В тот момент Ember казался магическим и ужасным одновременно. Магическим потому, что я раньше никогда не видел ничего подобного. Это был мой первый опыт с реактивным программированием и честно говоря мне потребовалось не мало времени чтобы понять, что множество классических паттернов разработки не всегда работают для реактивного программирования. Также ember-cli предоставляет поддержку ES6 из коробки, что делает написание кода на JavaScript удовольствием. Писать на том, что на тот момент называлось handlebars было ужасно и я как сейчас помню уродливость bind-attr, который нужно было использовать, чтобы связать свойство компоненты или контроллера (да, тогда контроллеры много значили!) с html-тегом. Да, работать с handlebars было иногда неприятно, но на дворе был 2014, когда React еще не был даже анонсирован и единственной реальной альтернативой, полноценным фронтенд-фреймворком, был AngularJS.

Прошло больше трёх лет и мы в Kallegorna сделали не мало проектов на Ember и я лично создал множество приложений на стороне от маленьких до огромных. Всё это время мне нравился Ember, потому что он делал меня супер производительным: если вы попросите создать прототип для клиента на Ember + Rails или на чистом Rails, я определенно выберу первый вариант. Мне даже не нужно API, я просто настрою адаптер для local storage, спроектирую модели как нужно, добавлю немного фабрик для тестовых данных и напишу прототип. А когда мы закончим с настоящим API, поменяем адаптер на тот, который ему соответствует.

Когда ты плотно работаешь с фреймворками, обновлять DOM становится так просто, что совсем не хочется возвращаться к традиционному подходу, вроде шаблонов Rails. Не будет преувеличением сказать, что Ember изменил мой образ мышления, особенно относительно сложных DOM-элементов вроде форм. Если handlebars был примитивным, HTMLBars, появившийся в Ember 2.0, достаточно мощный и вам не нужно что-то большее. Мне особенно нравится в Ember то, что ты имеешь дело с фреймворком и это уже дает множество преимуществ, например систему аддонов. Может потому что я изначально не фронтенд-разработчик, а может я просто не люблю изобретать велосипед, но я могу установить аддон и сразу же использовать его в шаблонах — это супер продуктивно.

React приключение

6 месяцев назад мне сообщили, что я буду работать с клиентом, который хочет создать новое фронтенд-приложение на React. Честно говоря, я был очень счастлив узнать это. Наконец я смогу попробовать эту штуку под названием React и его экосистему. Все вокруг говорили про React. Его паттерн data-down-actions-up (данные вниз, события наверх) был настолько революционным, что даже Ember адаптировал его (= добавил лучшую поддержку). Я не совру, сказав, что паттерны React сильно повлияли на Ember-сообщество. Ember вобрал вещи, которые вероятно имели наибольший смысл. Меня это очень радовало.

В первый день работы с React я сел и прочитал всю официальную документацию. После трёх лет опыта с Ember, я не только понял React за несколько часов, но также осознал общие паттерны и почему вещи работают так, как работают. В сущности я бы сказал, что React-компоненты не сильно отличаются от Ember-компонентов. Всё было очень круто и вдохновляюще.

К сожалению, со временем я стал осознавать, что React и его экосистема упускают некоторые ключевые вещи, которые есть в обычных фронтенд фреймворках, типа EmberJS, AngularJS, VueJS и остальных.

Webpack сложный, хотя и стал стандартом де-факто

В течение 6 месяцев я ни разу не прикасался к Webpack. Хотя я пытался понять его с помощью документации и туториалов, но так и не освоил его по настоящему. Похоже Webpack стал стандартом де-факто во фронтенд-разработке, но у меня никогда не было проблем с Ember. Ближайшим аналогом Webpack в Ember является Broccoli в комбинации с ember-cli. Оба инструмента очень легко понять.

С Ember можно создать и в буквальном смысле развернуть проект за 5 минут, например с помощью Surge (или даже используя серверный рендеринг с fastboot на Heroku). Думаю, это большое преимущество. Что касается Webpack, то я слышал много историй, как разработчики тратили от одного до трёх дней, чтобы настроить его правильно и это меня очень расстраивает :(

React хорош, но он только view

На случай, если вы ещё это не поняли, React сам по себе это прослойка для view. Если вам нужно многостраничное приложение, понадобится по крайней мере библиотека для роутинга, например react-router, и что-то для управления состоянием, вроде react-redux.

Хотя многим разработчикам, не одобряющим фреймворки, эта идея может понравится, помните, что делая ваш основной код зависимым от множества библиотек, придется терпеть их особенности или решения мейнтейнеров (например несовместимость API в следующем релизе…). И стоит учитывать, что библиотеки разрабатываются независимо друг от друга (и от React), делая поддержку кода с годами всё сложнее.

React-router не очень хорошая библиотека

React-router — стандартная библиотека для React и неожиданно не самая лучшая. Иногда react-mini-router или даже react-spoon (на момент написания этой статьи имеющий только пять звезд на GitHub) могут быть более мудрым выбором, чем react-router.

Обратная несовместимость в каждой версии

Мы использовали React-router V4 и догадайтесь, что значит V4: каждая версия переписывалась заново, не сохраняя обратной совместимости с предыдущими версиями. Разве это хорошо?

Для сравнения Ember-роутер почти не изменился за последние три года. Именно для этого и нужен движимый RFC подход к разработке библиотек и фреймворков. Вероятно, за исключением небольших правок объявлений роутов в файле router.js, обновление займет максимум полчаса и все изменения имеют предупреждения об устаревании (deprecation warnings) с информацией о необходимости сделать правки до того, как финальные изменения будут приняты в библиотеку и сломают обратную совместимость.

Нет именованных роутов

Что еще хуже, у вас не может быть именованных роутов, потому что это считается анти-паттерном. Я здесь единственный, кто считает, что именованные роуты необходимы современному веб-приложению?

Вот две причины:

  1. Я понимаю, что большинство сайтов написано на английском, но случается, что люди разрабатывают сайты на своих родных языках и URL тут не будет исключением. Я действительно должен писать на китайском каждый раз, когда мне нужно сделать редирект/ссылку на роут?
  2. Разве не лучше иметь одно место, где вы объявляете ваши роуты? С React-router кажется, что роуты разбросаны по коду и если вам придётся разбираться в существующем приложении на React, придётся открыть не мало файлов прежде чем вы разберётесь, что происходит.

Когда вы приходите в проект на Ember, первая вещь, на которую вы смотрите, это router.js, где объявлены все роуты приложения. Глядя только на этот файл, можно понять, как устроено приложение и быстро найти файл, в котором необходимо сделать изменения.

Что же можно сделать со структурой React-router, учитывая, что у нас нет именованных роутов? В теории, если использовать и объявлять роуты в правильных местах, то мы могли бы добавлять и удалять фичи, добавляя и удаляя файлы без дополнительной настройки. Мне ещё не приходилось делать подобное на практике с React, но интересно, сработает ли это, учитывая зависимость роутов от прочего кода.

Роуты !== компоненты

Мне не нравится подход React, что всё может быть компонентой. Кажется, этот подход не всегда работает и мне нравится, как это сделано в Ember. Вам определенно нужно, чтобы роуты (можете называть их компонентами страниц) заметно отличались от обычных компонент. У роута должны быть отличные от компонент хуки, обрабатывающие url-параметры, загрузочные/ошибочные состояния, механизм редиректа, авторизация роута, различные жизненные циклы, определяющие, что приходит первым и последним, когда мы входим в роут, и много других вещей. С этим нам должны помогать библиотека или фреймворк. В противном случае придется самим придумывать как это сделать и я не верю, что все разработчики способны написать чистый, поддерживаемый, тестируемый и масштабируемый код для создания этого.

React-компонент не всегда может выполнить роль обработчика роута.

Сложности с простым

Как сделать редирект после Redux-события? Ответ на Stack Overflow напоминает мне о преимуществах полноценного фреймворка.

В Ember это всегда было просто. Вы шлёте событие из компоненты вверх до роута, который ловит его и делает задуманный редирект. В одну строку кода.

JSX или Handlebars — выбор ваш

После работы с Handlebars, точнее с HTMLBars с версии 2.0, шаблоны JSX выглядели странными первый месяц, но потом я привык.

Если вы спросите меня сейчас, Handlebars или JSX, я отвечу, что меня устраивают оба, но если придётся выбирать, то я выберу Handlebars. Одна из причин — возможность копировать/вставлять чистый HTML и он просто будет работать. Ещё одна причина — легкость понимания назначения компоненты благодаря DSL-дизайну, в то время как JSX выглядит как грязный хак, но это возможно только моё мнение. Я бы также предположил, что фронтенд-разработчику/дизайнеру будет легче разобраться в .hbs файле, чем в JSX (который, кстати, часто приводил меня к спагетти-коду), но опять же, я могу ошибаться.

react-redux клевый паттерн

С самого начала мы понимали, что далеко не уедем на одном лишь React. У нас уже был React-Router, но нужно было что-то для управления со стороны, вроде сервиса в Ember, потому что вы не можете пробрасывать свойства по всем компонентам вниз — это будет не поддерживаемо. React-Redux был очевидным выбором: Redux очень популярен для работы с состоянием в React-приложении и мы слышали о нём повсюду. Даже работая с Ember, я слышал очень много о Redux и о том, что он стал в какой-то степени революционным подходом в работе с состоянием.

Эта библиотека действительно помогла разгрузить компоненты (они стали функциональными) и работать с ней приятно. Паттерн был всегда одинаковым: правильно инициализировать состояние, добавить событие, добавить редьюсер, если нужно, и использовать событие в компоненте. Некоторые вещи всё же давались мне с трудом с Redux, но в целом он мне очень понравился.

Единственная проблема, которую я заметил, это то, что если вы уже выбрали React-Redux, то замкнуты в этом паттерне. Сделать половину приложения используя React-Redux, а другую на MobX, будет чрезвычайно сложно, но я не уверен, смог бы фреймворк решить эту проблему.

По опыту могу сказать, что вам редко понадобится глобальное состояние в Ember. Вместо это вы будете использовать маленькие сервисы (которые по сути составляют ваше глобальное состояние). Я думаю что Ember даёт вам так много инструментов, что иметь глобальное состояние становится избыточным. Тем не менее, я не противник Redux и буду рад попробовать ember-redux.

Инфраструктура запуска приложения

Выполнить какой-либо важный код до старта приложения не так просто в React-экосистеме. Когда мне это действительно понадобилось, пришлось рендерить пустоту из корневой компоненты, делать там необходимое и затем обновлять всё состояние, а это приводит к ре-рендерингу всего приложения с применёнными изменениями.

В Ember это заложено дизайном. Фактически у вас есть два вида инициализации: инициализация приложения и инициализация экземпляра. Первая выполняется во время сборки приложения и предоставляет место, в которое можно добавить свои прототипы, вставить какой-то код и прочее. Вторая запускается во время старта приложения, помогая сделать такие вещи как A/B тестирование, зависящее от настроек пользователя.

И вновь, в экосистеме React отсутствие фреймфорка заставляет тратить время для решения тривиальных задач, вместо того, чтобы сосредоточиться на продукте.

Функциональные и паттерны ООП

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

Я слышал, что MobX ближе к принципам ООП и если вы не знакомы с функциональным программированием, он подойдёт вам больше.

Нет ничего похожего на ember-data :(

Я бы хотел чтобы ember-data была доступна как отдельная библиотека. Это штука просто волшебна и я действительно так считаю! Например, если вы хотите начать разработку своего приложения, но ребята с бэкенда еще не начали делать API, вы можете подключить locale storage или session storage адаптер и начать разрабатывать с ним (вместе с моками данных ember-date-factory-guy). Затем, когда API будет готов, нужно заменить аддон, который вы использовали на RESTAdapter или JSONAPIAdapter. Интерфейс, который будет использовать приложение, останется тем же.

Конечно вы можете сделать это и с React, но я сомневаюсь, что все разработчики способны создать чистые, поддерживаемые и тестируемые интерфейсы для такой архитектуры, при которой легко можно будет переключаться между разными адаптерами/сериалайзерами.

Даже если API, с которым вы общаетесь, не сделан полностью по REST, ember-data дает гибкость настроить общение с API с помощью ваших собственных сериалайзеров/десериалайзеров или даже перенастроить адаптер под ваши нужды, сохраняя при этом прежний интерфейс для приложения. Если API, с которым вы работаете, консистентен в ответах, то этот процесс настолько прост, что вам может потребоваться переопределить лишь пару методов.

Жаль, если эта библиотека останется только внутри Ember и не будет выделена как отдельная библиотека.

Чувак, ты говоришь, что Ember идеален?

Нет.

Ember далек от совершенства. Когда я только начал использовать Ember, у него было очень много проблем. Но со временем, прислушиваясь к сообществу, ему удается их решать. Фреймворку намного проще развиваться, не разочаровывая пользователей.

В любом случае, вот несколько вещей, которые каждый разработчик, по моему мнению, должен учитывать, когда начинает новый проект:

Фреймворк — это платформа

Фреймворк предоставляет вам самый полезный API, который вы можете использовать, когда вам нужно, без необходимости привязки к определенному паттерну. Эта причина, по который мы должны смотреть в сторону фреймворков вроде EmberJS/Angular/VueJS. Фреймворк — это платформа. Скучная платформа, скажете вы, но подобная платформа с хорошими примитивами и инфраструктурой — всё, что нужно для создания на её базе чего угодно. При этом можно фокусироваться на продукте и не изобретать колесо каждый раз.

Производительность не так важна, важен результат

Все гонятся за лучшей производительностью, но нужна ли она так сильно? Я не продался идее, что все приложения должны иметь идеальную производительность, используя библиотеки типа InfernoJS. Важнее доставлять фичи и поддерживать кодовую базу, которую в тоже время можно легко изменить, если изменятся требования.

Относительно производительности Ember. Сейчас она достаточно хороша для 95% фронтенд-приложений и учитывая, что LinkedIn постоянно улучшает GlimmerJS, одновременно уменьшая его размер, будущее для Ember выглядит оптимистично. Но если вам нужно больше производительности, то вы всегда можете попробовать новые Glimmer компоненты или просто использовать React внутри Ember. (Ну, а если вам нужно что-то экстримально быстрое, то наверняка вы уже эксперт во фронтенд-разработке и без проблем справитесь со всей React экосистемой)

Хорошая документация

Документация — один из важнейших аспектов для фреймворка или библиотеки. Она дает возможность пользователям использовать платформу по максимуму и, конечно, хорошая документация мотивирует проверять сначала её, прежде чем искать ответы в Google.

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

Стабильность

Стабильность ещё один ключевой аспект библиотеки или фреймворка. Взаимодействие с пользователями также очень важно (я смотрю на тебя Angular). Стабильность гарантирует, что вы не будете тратить деньги/время, чтобы поддерживать вашу кодовую базу в актуальном состоянии. В погоне за хайпом это часто упускается из виду.

Ember всегда был очень стабильным, особенно после версии 2.0: мы обновились до HTMLBars без единой ошибки, мы обновились до Glimmer VM с тремя предупреждениями об устаревшем API. Мы обновляли Ember очень много раз, тратя на это очень мало времени. Для Ember стабильность — часть основных принципов. Так что даже когда появилась новая структура папок (критичные изменения!), заблаговременно был выпущен инструмент командной строки, автоматически конвертировавший ваш устаревший проект в новый. Разве это не здорово?

Сообщество

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

Ember следует процессу RFC для новых фич и изменений, в котором может принять участие любой: вы можете создать свой RFC-документ, описывающий новый API, его детальный дизайн (детальная реализация может быть предоставлена позже), объяснить зачем это нужно, насколько это совместимо с текущим API, нужны ли какие-то критичные изменения и прочее. Затем каждый может участвовать в обсуждении: первоначальный документ преобразовывается в финальный через взаимодействие и вклад всего сообщества. И, когда этот документ будет принят, основная команда (core team) Ember приступает к реализации. Вы можете посмотреть принятые RFC на доске состояния Ember.

Заключение

Просто чтобы прояснить: React это здорово. Я буду счастлив использовать React на проекте, где будет нужен только React. Например, чтобы заменить шаблоны Rails/Phoenix на React или добавить его к существующему проекту. Хотя, возможно, я попробую glimmerjs, который является эквивалентом React в Ember (view прослойка Ember), но это в основном потому, что я уже знаком с Ember и мне нравится куда его двигает сообщество.

Но… использовать React (и его экосистему) для полноценного приложения с множеством роутов/страниц/состояний? Нет, спасибо. Я считаю это хайп и для фронтенд-разработчика существуют варианты лучше.

Слушайте наш подкаст в iTunes и SoundCloud, читайте нас на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.

Статья на GitHub

--

--