
Многие разработчики любят сравнивать React и Vue. Кто-то останавливается на одном из этих фреймворков и упрямо придерживается его, даже не потрудившись познакомиться с другой библиотекой, которую когда-то отбраковал. Зачастую дело во времени: чтобы по-настоящему освоить все входы и выходы системы, с нею нужно работать, бороться и расти.
Конечно, неэффективно распыляться между схожими инструментами, но разве вам не любопытно? Мне – любопытно.
В интернете найдется множество сравнительных статей о том, как создать приложение в жанре «список дел» или т.п. с Vue и React, но реальные проекты редко бывают настолько просты. В реальном приложении приходится позаботиться о маршрутизации, сохранении состояния, совместимости плагинов, т.д.
Я заинтересовался не теми отличиями, что содержатся в базовой части библиотек Vue и React, а тем, каковы особенности создания реальных приложений при помощи этих фреймворков. Какой инструментарий удобнее, скажем, при разработке одностраничных приложений?
Приложения
Я использую Vue уже около двух лет, а веб-разработкой занимаюсь лет восемь. Впервые попробовав силы с Vue, я решил, что буду учить ее «в открытую», выложив в опенсорс простое приложение для ведения заметок, где будет возможность аутентификации пользователя при помощи JWT, а также полный набор CRUD-действий с заметками. В пару к нему я писал бэкендовое приложение, сделанное с использованием Koa.
Хотя, я и не испытывал острой необходимости менять фреймворк, я рассудил, что было бы неплохо выучить React. Поэтому я переделал на React мое приложение koa-vue-notes и также выложил его в опенсорс. Подумал, что такой опыт как минимум расширит мои представления о JavaScript, а, может быть, и найду себе новый любимый инструмент.
Вот домашняя страница моего приложения. Сверху – вариант с React, снизу – с Vue:
Хотя, я использую Bootstrap в моих приложениях все реже, обычно я внедряю в мои приложения новый компонент Navbar, появившийся в Bootstrap 4. Пытаясь повторить такое в Vue, я обнаружил, что Bootstrap-Vue – наилучший вариант для реализации Bootstrap 4. В React мои опыты и исследования привели меня к reactstrap.
В данном случае нужно отметить, что в конечном итоге я не стал использовать в React сетку Bootstrap, а остановился на варианте grid-styled, лучше сочетавшемся с применяемыми у меня styled-components – подробнее об этом ниже.
В приложении можно осуществлять с пользователем операции signup/login/forgot/reset, а с его заметками — create/read/edit/delete. Войдите в систему под demousername и demopassword, если лень регистрироваться.
Сравнение каталогов с исходным кодом
Первые впечатления
При работе с React сразу же становится очевидна одна вещь: придется очень плотно иметь дело с JavaScript.
Я придерживаюсь минималистических тенденций и стараюсь избавляться от всего хлама, который мне не нужен. Поэтому легко понять, насколько привлек меня React своей дешевой и сердитой природой.
Сравнение React-Router и Vue-Router
React-Router – это активно применяемая система маршрутизации для React. Скорость у нее отличная, однако, имея с ней дело на практике, я сталкивался с некоторыми интересными проблемами. Базовая настройка совсем простая, хотя, я не фанат объявления маршрутов прямо в HTML, как это требуется делать в React-Router v4 (в более ранних версиях React-Router ситуация была иная).
Продолжая препарировать мои маршруты, я повстречал такую проблему: как не допустить пользователей на те страницы, к которым у них не должно быть доступа. Элементарный пример: пользователь пытается открыть страницу типа account, не выполнив вход в систему. Потребовалось потратить не один час на изучение ситуации и действия методом проб и ошибок, чтобы выдать окончательное решение с применением React-Router.
В конце концов, меня не устроило то, насколько запутанным и неудобным получился код для реализации такого простейшего функционала. Ниже приведен код, закрывающий пользователям доступ к определенной странице:

Vue-Router – это собственная библиотека Vue, предназначенная для маршрутизации. Мне в самом деле нравится, как там можно добавлять дополнительную информацию к определениям маршрутов прямо в файле с объявлением маршрута. Посмотрите, как я закрыл пользователям доступ к страницам в Vue-Router при помощи свойства requiresAuth в определении маршрута и проверил истинность в функции router.beforeEach:

Теперь, когда мы рассмотрим код Vue, он кажется немного пространным, но именно так он и описан в документации, поэтому мне не составило труда настроить приложение. Не могу сказать того же о коде React; там, чтобы довести до ума такое же решение, мне потребовалось несколько часов. Когда в приложении требуется запрограммировать такую существенную функцию, как недопуск пользователей на те страницы, которые им не положено видеть… на такую работу не должна уходить целая ночь.
Далее, когда я пытался собирать по URL некоторые данные со страницы Edit, я обнаружил, что в самой свежей версии React-Router такая возможность оказалась удалена. Меня это… разочаровало. Думаю, я понимаю, зачем это было сделано: данные в строке запроса поступают во всевозможных видах и формах, но, позвольте, если даже параметр по URL взять невозможно – это как-то чересчур. Мне пришлось скачать библиотеку qs, чтобы правильно разбирать URL, и в ней также нашлись свои процедурные причуды. Подробное обсуждение – здесь.
На все про все, чтобы во всем разобраться, я потратил лишний час. Не самая серьезная проблема, однако, она разительно отличается от того опыта, что мне выпал с Vue-Router, а именно: поищи в документации и реализуй решение в коде. Я не пытаюсь сказать, что с Vue – не жизнь, а сказка; просто в случае с React у меня сложилось впечатление, как будто путь выдался заметно тернистее, нежели я ожидал.
Сравнение Redux и Vuex
Redux – это самое популярное хранилище данных в React, построенное по шаблону Flux. Если Flux вам не известен, поясню: это паттерн проектирования, в целом, основанный на однонаправленном потоке данных, организуемом путем диспетчеризации действий изнутри приложения. Иными словами, он все поддерживает в порядке, когда вы пытаетесь обращаться к данным из всевозможных ваших компонентов и манипулировать этими компонентами.
Вот для примера файлы из нашего хранилища Redux, где мы создаем запись при помощи actions и reducer:

В принципе, идея такова: мы диспетчируем actions для срабатывания reducers, которые безопасно манипулируют данными из хранилища. Таким образом, каждый компонент может безопасно считывать данные и реагировать на изменения в них.
Vuex в мире Vue эквивалентен Redux. Обе библиотеки обладают в этой сфере по-настоящему великолепной собственной поддержкой. Вместо reducers Vuex использует mutations для безопасного обновления данных хранилища. Кроме небольших отличий в именовании, обе библиотеки очень похожи. Ниже я реализовал ту же самую функциональность в приложении Vue в src/store/note.js (естественно, оба примера немного сокращены):

Честно говоря, Redux показалась мне полезной и мощной библиотекой-хранилищем для React, вдохновленной принципом Flux. У меня возникли проблемы из-за лишнего стереотипного кода. Естественно, теперь, когда я во всем разобрался, все кажется простым и ясным, но опыт подсказывает, что новичку в обращении с Redux будет сложно реализовать четкий и лаконичный код для React.
Например, приходится изучать и устанавливать библиотеку redux-thunk, чтобы диспетчировать действия от других действий, и для меня это был неприятный поворот. Конечно, я потратил еще пару часов, размышляя, а не воспользоваться ли redux-saga или redux-observable вместо redux-thunk. Вот тогда у меня мозги заскрипели, ощущение как раз можно описать словом thunk.
Это была сквозная тема данного проекта. Вспомните для сравнения интеграцию с Vuex – я, например, помню, как ловил себя на мысли «неужели это со мной?», налаживая все это впервые – а я к тому моменту еще даже не успел познакомиться с паттерном проектирования Flux.
Рендеринг
Из всех деталей React страньше всего мне показалась функция рендеринга. Во Vue так просто перебирать данные и изрыгать элементы, либо отображать/скрывать данные в зависимости от переменных состояния/хранилища. В React казалось довольно неестественным, что приходится создавать цикл заметок вне рендерера.
Во Vue, если вы желаете что-либо отобразить или скрыть, просто сделайте так:

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

Код чуть длиннее и не поддерживает так кстати пришедшейся возможности циклического перебора, которая во Vue может быть организована при помощи v-for. Но, конечно же, когда я освоился с выполнением этих маленьких простых вещичек, они перестали казаться такими странными. В общем, привыкнуть можно, просто именно так это делается в React. Но, нельзя не отметить, насколько легко во Vue устроен доступ к данным в конкретном макете страницы. Кажется, что на самом деле маленькие вспомогательные функции по душе React.
Styled-Components
Знаете, что мне больше всего нравится в этом проекте? Styled-Components. Мне по-настоящему импонирует инкапсуляция, которую они обеспечивают. Да, во Vue можно приколоть свойство scoped в разделе вашего компонента и, в принципе, сделать то же самое.
Было что-то действительно гладкое и приятное в том, как каждый компонент превращается в маленькую «вещь в себе». Возникают небольшие сложности с передачей произвольных свойств (props), но, уладив с ними некоторые детали, работать стало приятно. Помню один пользовательский комментарий, отлично ухвативший эту мысль: «приучает тебя заранее продумывать, как будешь оформлять компоненты».
Думаю, веская причина, по которой пользователь React действительно легко в это врубается – в том, что ранее оформление компонентов было устроено несколько неуклюже. Думаю, нас немного разбаловали целым миром Однофайловых Компонентов, доступных во Vue. На этом проекте я тем более смог оценить Однофайловые компоненты – поистине убийственно хорошая фича.
Сравниваем Create-React-App и Vue-CLI
Мне действительно понравилось create-react-app. Притом, какой я фанат vue-cli, вариант create-react-app – его достойный конкурент. Рекомендую всем пользователям установить экземпляр Webpack с нуля, чтобы разобраться в деталях. Но, если вам требуется что-то солидное для продакшена, настоятельно рекомендую использовать готовые инструменты скаффолдинга.
Инструменты разработки
Также отмечу: инструменты разработки в Redux и React определенно не так хороши, как инструменты Vue, это касается как оформления и цвета, так и необходимости раскрывать гигантское дерево компонентов, просто чтобы посмотреть состояние компонента. Мне в таком режиме было довольно тяжело следить за переменными приложения.
Может быть, я что-то упускаю или пользовался версией, которая сейчас не считается в сообществе действующим стандартом. Инструменты Vue кажутся мне без преувеличения потрясающими, сработанными хорошо и на совесть, а также визуально приятными. Учитывая, сколько времени придется тратить на работу с этими инструментами, вы поймете, как важны подобные мелкие детали.
Заключение
При прочих равных, я очень рад, что потратил время на изучение React. Знаю, я по-прежнему криворук и в работе с ним, и в программировании вообще, но, как минимум, теперь я освоил некоторые сложные вещи и познакомился с концепциями. Также я планирую попробовать React Native, на случай, если в будущем придется заняться разработкой мобильных приложений. Такой опыт точно не помешает.
Я мог бы бесконечно вдаваться в сравнение мелочей. На самом деле, эта статья – лишь малая толика того, что можно сказать о сравнении Vue/React. Поэкспериментируйте с приложением – мне его активно комментировали, и эти советы и подсказки пригодятся и вам.
Итог: мой следующий проект я делаю на Vue. С React управиться можно, но с виду комплектация у него чуть послабее. На первый взгляд это даже может понравиться, но, как только вы разберетесь, что к чему – сразу поймете, что пишете явно больше кода, чем на самом деле необходимо.
» Ссылка на предзаказ книги
Бумажная версия появится в конце марта.
Для Хаброжителей скидка 25% по купону — Vue.js
iit
Для React аналог Vuex это не разу не Redux а скорее Mobx а точнее MobxStateTree.
mobx-state-tree
Vuex
questor
Попробовал открыть оглавление книги, чтобы посмотреть заголовок тем, но увы:
Можете рассказать вкратце, что по сравнению с официальным руководством будет сверх в книге? На 304 можно и воды налить, разбавив оф. гайд, а можно и достаточно интересные подробности рассказать.
TheShock
В оригинале код вставлен текстом, а вы его вставили скриншотами. Вы, блин, издеваетесь? Это же код. Банально, если хочешь прокомментировать статью — ты не можешь вставить адекватную цитату. Что за идиотская мода на скриншоты кода вместо кода?
dmitryrf
Отсутствуют иллюстрации из оригинальной статьи.
vintage
Что ж, давайте разберём роутеры. В реактовой версии смешивается роутинг и инициализация компонента. Из-за этого, если мы хотим отобразить один компонент для разных роутов, то придётся копипастить передачу всех его параметров в каждом месте использования. Обратите внимание, как автор избегает этой проблемы путём редиректа. Пользователь будет несказанно "рад" увидеть вместо формы авторизации домашнюю страницу.
Далее, в вуешном роутере зачем-то для каждого роута указывается ещё и заголовок страницы. Зачем это роутеру? В заголовке страницы пользователь ожидает увидеть название заметки, а не абстрактный "Note". И опять же, указывается компоннет без параметров. Кастомные мета-поля — это, конечно, замечательно, но что насчёт статической типизации и подсказок IDE? Одна опечатка и проверка авторизации отваливается.
Теперь роутинг здорового человека:
Так как инициализация и композиция разделены, то нам достаточно лишь указать "тут должен быть такой-то вложенный компонент". Если пользователь не авторизован или не является автором заметки, то показываем форму авторизации.
TheShock
То, что вы привели — очень похоже на God Object, но в Реакте роутинг — отвратительный, тут сомнений нет.
vintage
Где ж вы тут божественный объект углядели?
TheShock
Сильно много ответственности. Тут и роутинг и бизнес-логика для каждого роута.
vintage
Роутинг — это и есть «бизнес-логика», определяющая какие экраны показывать в какой момент времени.
TheShock
Какие экраны зависимо от пути. Зависимость от других переменных — это уже не роутинг.
Но в вашем примере оно хоть в правильном месте лежит — в клиентской модели, а не во вьюшки, как это сделано в Реакте.
vintage
Не важно как это называть, суть-то одна — какие экраны показывать в зависимости от разных состояний.
Да нет, это вполне себе вьюшка, решающая какие вьюшки показывать внутри.
VolCh
Мне нравится подход, когда изменение URL лишь вызывает изменения в каком-то стейте, а роутинг — чистая функция, принимающая стейт и возвращающая готовый элемент.
TheShock
Я вообще стараюсь абстрагировать от пути в браузере. Допустим статья у меня находится по адресу
/article/123
.Для неё есть роут (далее псевдокод):
Когда открывается страница браузера или меняется путь — роутер (который находится в слое модели) просчитывает, какой роут больше всего подходит. В данном случае в роутер будет присвоено что-то вроде такого:
Это, естественно, MobX-observable свойство, которое при изменении автоматически перерисует все зависимые вьюшки.
Ссылки на странице я указываю не как
а как
а если надо вручную переместить пользователя куда-то, то делаю не
а
Мне нравится абстрагированность от путей (везде кроме маппинга) и статическая типизация во всём этом. А еще, при необходимости, можно узнать текущий роут в любом компоненте без костылей, ведь он — часть стейта, как и все остальные данные.
А вьюшка рендерится как-то так:
DarthVictor
На каждый роут в приложении — свой метод одного и того же объекта. При 30 роутах у объекта будет 30 методов?
Если уж начали про роутинг здорового человека писать, то укажу, пожалуй universal-router.
vintage
Если у вас на одном уровне иерархии 30 вариантов вложенных компонент, то будет 30 методов, да.
akeinhell
Сразу видно человек не пишет на реакте и сравнивает с ним свой любимый vue
На счет реакт-роутера есть очень логичное решение.
делаете HOC, который инкапсулирует в себя всю логику с проверками на авторизацию, ACL и т.д.
А потом очень просто это все подключаем
при этом вместо редиректа, мы можем спокойно показать форму логина на приватном роуте
nuit
> Сразу видно человек не пишет на реакте и сравнивает с ним свой любимый vue
Тут даже хуже, человек не владеет жаваскриптом, тк для композиции в реакте не нужно изучать реакт.
strannik_k
Давно не работал с реакт роутером, но вместо HOC
своя обертка над Route, на мой взгляд, будет немного получше:
TheShock
А как два HOC'а замените? Это ведь классические композиция-против-наследования.
strannik_k
Я имел ввиду не наследование CustomRoute от Route, а рендерить Route внутри CustomRoute.
Сейчас понял, что фактически предложил HOC, но для Route, а не для PostEdit.
vlviking
А мне, чем Vue больше нравится, так это Enter/Leave & List Transitions. Очень удобно работать с простыми анимациями. В реакт сложнее.
sshmakov
Может я что не понял — человек ограничения доступа неавторизованным пользователям реализует на фронте? Серьезно?
TheShock
Ну это нормально. Надо выдать вменяемую страницу, а не просто админский шаблон с незагруженными из-за 403 данными. Просто на сервере это тоже реализуется
vintage
Есть мнение, что такой шаблон надо показывать не в зависимости от роутов, а если от сервера пришёл 403 ответ. Тогда логика проверки прав будет в одном месте, а не размазана между фронтом и бэком, с периодическим разъездами то в одну, то в другую сторону.
TheShock
В теории оно то, конечно, так, но на практике оно так не получается. Все-равно на клиенте надо делать пре-валидацию для более удобной работы. Да и банально зачем делать запрос и ждать ответ, если клиент и сам может знать, что этого делать нет необходимости.
Другой пример — валидация полей форм, которую нужно делать на сервере и можно сделать исключительно там, но от этого страдает ux.