Pull to refresh

Comments 73

React дал ясно понять, что движется в сторону функциональных компонент, а не в сторону компонент основанных на классах, что отнимает половину всей мощи TypeScript.

На самом деле это правильно. Компоненты-классы ну просто не могут не нарушать SRP, потому что как минимум они рендерят картинку. И "делать кроме этого что-то ещё" — так себе идея. Потому выносим всю логику в отдельные классы, а представлению (реакт-компоненту) оставляем его единственную обязанность.

Только нормальные разрабы и в классах норм напишут, при этом не потеряют удобства классов, а говнокодеры и в функциях логики наплодят

Так чем функциональные компоненты спасают от SRP? Голые может быть и спасли бы, но в этом случае вы не способны реализовать полноценный компонент, либо это затребует слишком большого оверхеда в виде большущей куче мелких компонентов, которые вы более нигде и никогда использовать всё равно не будете.

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

А ведь какой-нибудь простенький компонент может к примеру запрашивать по http данные, это в React будет реализовано в useEffect на старте или в useCallback по клику мышкой. Как вы будете подменять данный код в тестах? У вас просто нет легальной возможности это сделать.

P.S. Мы сейчас работаем над визуальной IDE на React, с кучей готовых компонентов, изначально я не хотел выбирать React + Redux для этой задачи, но сверху сказали. Теперь проект превратился в какой-то адский ад. Тестировать невозможно. Вынести адекватно бизнес-логику совершенно отдельно от React и Redux просто невозможно, а её ну очень много. React не поддерживает IoC и мы в общем не можем вынести общие сервисы для работы в рамках компонентов и редьюсеров, всю логику нельзя унести в редьюсеры, это потребует очень большого оверхеда. Ах да, структура приложения древовидна, редакс в принципе не предназначен для этого, любой чих в итоге — полная перерисовка всего. Потребовалось делать подсистему кэширования. В итоге проект превратился в сборище костылей лишь потому, что выбран неподходящий ниструмент.
React + Redux

Всё указанные трудности — из-за этого. Вы не умеете готовить Redux )) Я тоже не умею. Попробуйте связку React + TypeScript + MobX, там можно напроектировать хорошую ООП-структуру приложения — именно логики и связанного с логикой состояния, начисто убирая их из компонентов, делая правильные зависимости через DI, по всем канонам. Это будет совсем другой Реакт, в котором не найдется места компонентам-классам.

делая правильные зависимости через DI, по всем канонам.

О ужас, DI во фронтенде редкостная дичь. Ещё и расписали это как «правильное» и «по всем канонам», хахах,
Возможно для тех, кто любим тебе усложнять жизнь на ровном месте да, им подойдет эта дичь и не только, надо идти глубже и брать ещё и Redux и т.п. для полного «кайфа».

Ну давай, расскажи, как же готовить Redux для работы с древовидными данными? Когда конечное приложение - дерево. И уж MobX не надо тут рекламировать, я понимаю, он вам нравится, но минусов у него достаточно. Вообще в нашей архитектуре куда лучше подойдёт RxJS, хотя смотрим сейчас на Effector и потихоньку рефакторим приложение, для безболезненного перехода, точнее менее безболезненного. Ибо бизнес-логику надо будет перелопачивать, знатно перелопачивать.

А вот DI должен быть либо везде, либо нигде, ведь идея в том, что создаётся только базовый объект приложения, далее всё разруливает именно IoC контейнер. В ином случае просто теряется весь смысл. О функции "resolve" надо забыть, вызываться должна ровно 1 раз. В случае с React этого добиться невозможно. Собственно выйдет знатный антипаттерн.

Ещё минус React, не скажу как внутри, но снаружи он синхронен, в этом его колосальная проблема. Так или иначе появляется слой перехода асинхронного и синхронного. Кругом нужны костыли для поддержки полноценной асинхроннсти. После к примеру WPF и ASP.NET MVC на это очень больно смотреть.

Ну давай, расскажи, как же готовить Redux для работы с древовидными данными? Когда конечное приложение — дерево.

Как минимум не использовать Redux. Вообще в приложениях любого типа его нельзя использовать, но с другой стороны если хочется ныть как все плохо, то используйте конечно.
Я конечно понимаю, если взять MobX, то больше не будет проблем и жизнь станет легка и непринужденна, но ведь тогда не получится ныть и искать адовые пути решения элементарных задач.
Вот к примеру ваша, абсолютно элементарная «проблема» — древовидная структура данных, и что? Где проблема-то? Mobx просто создан для этого. Посмотрите на досуге что такое javascript Proxy, уже древняя штука по меркам 2021 года.

state.cars[0].passangers[4].name = 'new name'; и все, перендерится только один конпонент, вот прям только тот, который читает «name» у объекта этого пассажира.

Но нет же, это же так легко и просто, вы лучше пройдете адовый путь впаре с RxJS и/или Effector'ом и заодно разорите работодателя, заведомо обреча код проекта на провал и полное переписывание с нуля.
И уж MobX не надо тут рекламировать, я понимаю, он вам нравится, но минусов у него достаточно.

Какие минусы? Перечислите пожалуйста, и желательно реальные минусы, а не бред сивой кобылы или джунов которые что-то где-то прочитали/увидели и решили что раз так написали, значит это не просто так и действительно так оно и есть.
Например периодически встречаю псевдо «минус» — «он мутабильный», смешно и абсурдно конечно, но реально кто-то так думает.
Вообще в нашей архитектуре куда лучше подойдёт RxJS, хотя смотрим сейчас на Effector и потихоньку рефакторим приложение

Сочувствую, боль, разочарование и кандидат номер один на полное переписывание с нуля из-за невозможности дальнейшего развития/поддержки будущего «чуда», а так же невозможность нанять адекватных разработчиков т.к. разумеется с такой белебердой никто иметь дел не будет.

P.S. выше вы писали:
изначально я не хотел выбирать React + Redux для этой задачи, но сверху сказали.

Что я могу сказать, прогибаться и потом страдать, при том что сразу же знаешь наперед что будешь страдать, так себе жизненная позиция… Вы сами себя так поставили и на этом месте работы на вас будут ездить.
У меня например все просто, как только приходит какой-то неадекватный ультиматум или проект на котором я «должен теперь работать» «сверху», я просто перехожу на другую работу. Зачем страдать если можно не страдать? Благо предложений на рынке завались. Другое дело если бы у вас была такая профессия где предложений нет вообще, то от безисходности придется потерпеть т.к. тут уже вопрос умереть с голоду или нет)
Несмотря на такую агрессивненькую подачу материала, я в целом согласен. ReactiveX имеет высокий порог входа и для большинства команд это мина замедленного действия. Сегодня работает а завтра ушла пара людей с техкомпетенциями и приплыли. MobX не прост, но он, сцуко, работает ) И да, если молча соглашаться на то что предлагают сверху, это прямой путь в психоневродиспансер. Потому что в случае успеха — это успех кого надо успех, а косяки это только твои косяки.

Какие минусы? Перечислите пожалуйста, и желательно реальные минусы

Большая проблема MobX, это оборачивание в Proxy для реализации observable, собственно это ещё грозит потерями ссылок, в итоге объекты уже не сравнить по ссылкам после MobX. При этом оборачивание в глубину. Я конечно понимаю, что можно вырубить, но тогда теряется весь смысл MobX. Производительность от этого местами страдает ничем не меньше чем с Redux. Если уже и смотреть, то лучше в сторону MST (MobX State Tree), он не оборачивает в Proxy, но требует строгого описание типов, что на деле другая, совсем не маленькая проблема.

Почему в нашем случае MobX не подойдёт, у нас хранятся таблицы, временами на сотни тысяч строк, если это ещё заворачивать в Proxy, аааа, будет кошмар, оно и без этого сильно тормозит при обработке таблиц. В нашем случае иммутабельность даже является спасением.

Большая проблема MobX, это оборачивание в Proxy для реализации observable, собственно это ещё грозит потерями ссылок, в итоге объекты уже не сравнить по ссылкам после MobX.

const response = await apiRequest(`GET /lalala`);
someStore.items = response.items;

Вот как бы и всё, о какой ещё потери ссылок вы говорите? Вы должны работать с someStore.items.

Если уже и смотреть, то лучше в сторону MST (MobX State Tree), он не оборачивает в Proxy, но требует строгого описание типов, что на деле другая, совсем не маленькая проблема.

Ну уж нет, MST это убожество, тем более если речь идет о производительности, то у MST она самая худшая.

Почему в нашем случае MobX не подойдёт, у нас хранятся таблицы, временами на сотни тысяч строк, если это ещё заворачивать в Proxy, аааа, будет кошмар, оно и без этого сильно тормозит при обработке таблиц. В нашем случае иммутабельность даже является спасением.

1) Хоть на миллион, с чего вдруг они все должны быть разом добавлены в DOM дерево, если пользователь их не видит глазами, пока не начнет скролить?
2) Да вы можете вообще без стейт менеджеров обойтись и будет у вас максимум производительности. Вот наводка и рабочий пример, вообще без стейт менеджеров и на любой объем данных: codesandbox.io/s/adoring-nightingale-5m227?file=/src/App.tsx
Суть приема думаю ясна.

В нашем случае иммутабельность даже является спасением.

Иммутабельность всегда медленее мутабильности и прожорлевее по памяти, сейчас речь не о MobX, а вообще в целом.
Почему в нашем случае MobX не подойдёт, у нас хранятся таблицы, временами на сотни тысяч строк, если это ещё заворачивать в Proxy, аааа, будет кошмар, оно и без этого сильно тормозит при обработке таблиц. В нашем случае иммутабельность даже является спасением.

Для справки: кроме обычного observable (которое по сути своей observable.deep), есть ещё observable.ref и observable.shallow. Первый отслеживает только значение наблюдаемого поля, но не залазит внутрь. Второй залазит на один уровень в глубину. С помощью observable.ref и computed можно запилить абсолютно ту же самую схему с иммутабельностью, которая делается в редуксе со стейтом и мемоизированными селекторами.
Вышеупомянутый observable.deep, надо заметить, тоже не всегда "deep". Он не залезает внутрь произвольных объектов (которые не Object и не Array).


Таким образом, глубиной наблюдения можно довольно тонко управлять. И основная идея — стараться делать замены по возможности на "границе наблюдения". То есть, например, у вас развесистое дерево observable.deep, тогда если просто взять и поменять от корня (в иммутабельном стиле) — долго, МобХ должен обойти всё, посносить везде подписки, если есть. А потом обработать новый объект. А вот листики меняются быстро.


С MST не работал, но вроде это просто нашлепка поверх обычного мобикса, то есть в основе тот же механизм. Может, ошибаюсь.

Ну давай, расскажи, как же готовить Redux для работы с древовидными данными?

Ну написал же, что не умею. Чего глумиться-то?..


И уж MobX не надо тут рекламировать, я понимаю, он вам нравится, но минусов у него достаточно.

Какие, например, у него минусы?


Вообще в нашей архитектуре куда лучше подойдёт RxJS, хотя смотрим сейчас на Effector

Выглядит, как будто вы заблудились в названиях и не знаете что именно хотите.


А вот DI должен быть либо везде, либо нигде, ведь идея в том, что создаётся только базовый объект приложения, далее всё разруливает именно IoC контейнер.

Ну вообще, DI — идея, IoC контейнер — реализация. Реализация для идеи, но не наоборот.
Да, в реактовские компоненты надо вручную, хуками, затаскивать функциональность, ну и что? Это не так страшно выглядит.


Ещё минус React, не скажу как внутри, но снаружи он синхронен, в этом его колосальная проблема.

Что значит "синхронен"? Это слой представления, он не должен мутить всякую асинхронщину. По сути — просто визуализация текущего состояния, плюс обработка действий пользователя. Не надо пилить логику на Реакте, тогда не будет трудностей.

Ну вообще, DI — идея, IoC контейнер — реализация.

IoC контейнер — это костыль ко кривой идее DI (которая является реализацией идеи IoC), чтобы превратить её в более адекватный подход к IoC — CL.
https://en.wikipedia.org/wiki/Inversion_of_control#Implementation_techniques


По сути — просто визуализация текущего состояния, плюс обработка действий пользователя.

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

По сути — просто визуализация текущего состояния, плюс обработка действий пользователя. Не надо пилить логику на Реакте, тогда не будет трудностей.

Даже сделать страницу, которая показывает некий документ с сервера. Так или иначе в в стартовом useEffect надо инициировать запрос, потом надо собственно дождаться ответа, проверить не размаунтился ли уже компонент, обновить состояние. Это уже асинхронщина.

Идём дальше, у нас master-slave представление, где детали подтягиваются с сервера непосредственно при выборе элемента в master представлении.

Идём дальше, у нас есть чарт, который должен регулярно обновлять данные, система мониторинга.

И всё это должен инициировать некий компонент, обрабатывать, оно само по себе жить не будет.

А идём ещё дальше, необходимо обрабатывать много данных, заводим WebWorker, в нём обработка, UI не тормозит, но ожидать результат требуется, это опять асинхронщина.

Так или иначе в в стартовом useEffect надо инициировать запрос, потом надо собственно дождаться ответа, проверить не размаунтился ли уже компонент, обновить состояние. Это уже асинхронщина.

Повторюсь: не надо пилить логику на Реакте. Все эти запросы, промисы, ожидания и прочее — на уровне логики. Представление само по себе не должно делать запросы и (особенно) чего-то дожидаться, а потом ещё проверять на размаунт. Представление читает стейт, а так же следит за стейтом и за действиями пользователя. А стейт может содержать полученные данные, либо состояние загрузки/обработки или ошибки. Но сами по себе эти сведения "синхронные", в готовом виде.


Возьмем, к примеру, модный нынче useQuery. Думаете, он возвращает промис, раз он "query"? Нифига. Он возвращает реактивную структурку с разной хренью, а запросы и кэширования делает на уровне своей модели, живущей где-то в контексте.

На правах шутки (см. статью) берите Vue ;)

Вполне может быть и доля правды в шутке.

Почему мы должны выбросить шило и взяться за мыло.

Для лёгкой интеграции с RopeServiceModuleFactory.

Мне вот вообще не показалось что Angular удобнее в разработке. Исторически так сложилось что познакомился с ним раньше чем с React, то есть предвзятость была как раз в сторону Angular. Но спустя год жизни с React для меня вопрос что использовать даже не стоит. React гибче, у реакта ниже порог входа для новых разработчиков. React + TypeScript + MobX прекрасно заходят для разработчиков с опытом в backend. Прокидывание переменных в Ангуларе это боль. Все эти Input Output и ExpressionChangedAfterItHasBeenCheckedError забыл как страшный сон.

Архитектура сама себя не сделает ни там ни там. Можно породить кошмар на ровном месте и в Ангуларе пожалуй это сделать проще. Библиотеки в реакте довольно типичны — Formik, Yup, axios/superagent. Голову ломать не приходится. Есть желание эксперименитровать — подключай новую и вперед. Хочешь не думать — выбираешь классику.
видимо вы плохо знакомы с Angular. Я ExpressionChangedAfterItHasBeenCheckedError видел последний раз пару лет назад и то в чужом коде.
Никаких проблем с прокидыванием переменных нет, наоборот, это в Реакте с этим проблемы, если костылями не обматываться. Впрочем все это тривиальщина и легко решается в любом фв.

По вашим словам ясно, что вы не изучили angular. Он намного гибче реакт

Примеры, мне нужны примеры! Тогда и станет ясно, может быть это вы не изучили Реакт.
Прокидывание переменных в Ангуларе это боль. Все эти Input Output

1. В этом легко разобраться и это очень логично, особенно когда надо сделать внешний ивент отличный от onclick/onchange
2. Вас никто не заставляет это использовать, есть ngxs, ngrx, akito и куча других стейтов.
3. Output гибче в отличии от обычных props в реакт, например мы можем в ts написать this.component.outputEvent.subscribe(//code) так же можем внутри самого компонента в одном месте эмитить значение, в другом делать что-то с этим значением, но это зависит от того являетесь ли вы сторонником rx подхода или нет.

ExpressionChangedAfterItHasBeenCheckedError забыл как страшный сон.

Не видел такой ошибки года 3

Архитектура сама себя не сделает ни там ни там. Можно породить кошмар на ровном месте и в Ангуларе пожалуй это сделать проще. Библиотеки в реакте довольно типичны — Formik, Yup, axios/superagent. Голову ломать не приходится.

Только вот в angular есть по дефолту отличный httpclient, который хорошо интегрирован и типизирован.
Так же там есть:
1. Роутер
2. Реактивные формы
3. Обычные формы
4. Control Value Accessor очень удобная штука, с ее помощью любой компонент может иммитировать браузерные
5. Пайпы
6. Директивы
7. Angular cdk который очень упрощает разработку, например модалки, порталы итд
8. Поддержка PWA
9. DI
10. rxjs и мало того, сам angular построен на нем, а не сбоку прикручен
11. Отличный cli
12. Schematics
13. Модульность
14. Компоненты поддерживают стили + изолируют их при необходимости

Так же мне нравится что:
1. Angular не боится ломать обратную совместимость, только так фреймворк может развиваться.
2. Обновляться легко за счет ng update в angular cli, главное не затягивать с обновлением, сразу с 4 на 12 версию будет тяжело обновиться. а с 11 на 12 займет пол часа.
Вас никто не заставляет это использовать, есть ngxs, ngrx, akito и куча других стейтов.

Наличие кучи альтернатив не иллюзорно намекает, что в датском королевстве что-то не то.

Там в примере был react + mobx против передачи данных через input/output в angular, я лишь написал, что в angular можно так же писать со стейтом и не использовать input/output.
Извините, я вижу перечисление «фич» но не вижу гибкости. Что можно сделать на ангуляре такого что будет сложно повторит на реакте? То что вы накидали список, так это сложность а не гибкость. И если положим вы на год-два уйдете с ангуляра на другой стек, все вышеперечисленное из достоинства превратится в недостаток. Потому что обратная совместимость сломана и ваши знания частично забылись, частично превратились в тыкву.
Что можно сделать на ангуляре такого что будет сложно повторит на реакте?

Директивы, структурные директивы, пайпы как минимум.

Ещё раз, директивы и пайпы это инструмент. С помощью инструмента мы решаем задачу пользователя. Хуки в реакте не самоценны, равно как и пайпы в ангуляре. Какую пользовательскую задачу помогают решить эти инструменты?

Ну например можно сделать так, showErrorsDirective возьмет внутри себя все инпуты и в случае ошибки выведет сообщение под инпутом, как подобное сделать в реакте?
Тут нет реализации самой директивы, а лишь пример использования.
<form showErrosDirective>
  <div>
    <label>Имя</label>
    <input type="text" formControlName="name">
  </div>
  <input type="text" formControlName="name1">
  <input type="text" formControlName="name2">
  <input type="text" formControlName="name3">
  <input type="text" formControlName="name4">
</form>


Ну а по поводу:
С помощью инструмента мы решаем задачу пользователя

Все задачи пользователя можно решить на чистом html, css, js, смысл это обсуждать в контексте реакт и angular? Важно именно удобство разработки

Спасибо за пример, подумаю на досуге

Ну например можно сделать так, showErrorsDirective возьмет внутри себя все инпуты и в случае ошибки выведет сообщение под инпутом, как подобное сделать в реакте?

А он зайдёт за инпутами во вложенные компоненты? А для кастомного контрола сможет ошибку нарисовать? А как поймёт для каких инпутов надо выдавать ошибки, а для каких — нет? А сами ошибки он откуда возьмёт? А куда он засунет ошибку при горизонтальном раположении некоторых инпутов?

  1. Компоненты изолированы друг от друга, внутрь нет

  2. Для кастомного, да

  3. В компоненте создана реактивная форма, у каждого поля своя валидация

  4. Ошибки возьмёт из конфигов модуля, у каждой ошибки есть свой код

  5. Для горизонтального расположения он тоже сделает снизу, но по желании тут можно добавить ещё одну директиву, для позиционирования

Мне в голову приходит несколько вариантов, остановлюсь на наиболее «чистом». Используется Formik, делаем компоненту FormErrorFooter. Внутри через хук useFormik цепляем все ошибки валидации и выводим. Можно зацепиться на состояние touched поля чтобы показывать ошибки только тех полей, который пользователь «тронул».
<Form>
  <div>
    <label>Имя</label>
    <input type="text" name="name">
  </div>
  <input type="text" name="name1">
  <input type="text" name="name2">
  <input type="text" name="name3">
  <input type="text" name="name4">

  <FormErrorFooter/>
</Form>


Ну и маленький дисклеймер в конце, я так-то не фронтенд разработчик. Уверен здесь (на хабре) можно получить куда более профессиональное решение

А теперь сделайте так чтобы ошибка была под каждым полем

Я обычно так и делаю, просто вместо голого input у меня сразу компонента ввода со всем обвесом.

<CustomInput name='name1'/>

Внутри такого компонента примерно 6-10 строчек кода. Хук формика useField и верстки малёк. Могу скинуть пример если интересно.

Так у вас инпут отвечает за вывод ошибок, а надо наоборот

С чего это вдруг должно быть наоборот?? Компонент инпута формы (точнее обертка для простого <input />) для того и компонент, что может показать ошибку, если его не корректно заполнили, так вообще-то правильно, и должно быть именно так.
  1. Разделение ответственности, инпут не должен заниматься ни валидацией ни выводом ошибок, только отображение

  2. Если вы поставите внешний пакет с инпутом/любым другим контролом - придётся ему делать обертку и опять писать обработку ошибок

  3. Если надо задать конфиги для вывода ошибок, например показать ошибку сбоку, а не снизу, то придётся пробрасывать конфиги из инпут в компонент ошибок

1. Ещё раз повторяюсь, это не инпут, это обертка, внутри которой в том числе лежит инпут.
2. Нет, т.к. это просто обертка, подставил туда другой инпут и все.
3. Это обертка
 <FormInput value={formData.firstName.value} errorPlacement="left" errorMessage={formData.firstName.errorMsg} onChange={formData.firstName.onChange} />

Что сводится к
 
<FormInput formField={formData.firstName} errorPlacement="left" />
<FormInput formField={formData.lastName} errorPlacement="top" />
<FormInput formField={formData.email} errorPlacement="bottom" />


вот условно в таком духе.

Вообще не понимаю чего вы из тривиальной задачи делаете проблему, в React + MobX есть все инструменты чтобы сделать максимально круто и в различных исполнениях.
Давайте не будем про вкусовщину. Принцип единственной ответственности это конь в вакууме. Как ориентир сойдет, но как практическое руководство — никак нет. Что бы вы не написали, я могу придумать почему это нарушает SRP.

Вы дали сценарий, я написал решение. И будьте уверены на React + MobX у меня есть из чего выбрать. Я ориентируюсь на две метрики — time to market и cost of ownership. С ними все более чем в порядке.

Если бы вы поставили задачу конфигурации вывода ошибок, я бы непременно это учел. Но вменять вину за невыполнение не озвученных требований это моветон.
Вы мне написали, чего такого можно сделать в angular, что нельзя в реакте, я вам ответил, а теперь вижу оправдания про «так не было озвучено и так лучше»
в Angular возможны оба подхода и подход с директивой удобнее, так как директива работает с любым контролом и не нужно писать обертки для оберток.

Что, даже с текущим компонентом директива уже работает? А то раньше надо было или копипастить директивы в каждом месте использования компонента, либо делать матрёшку из нескольких бессмысленных дивов.

Обычные директивы всегда и везде работают, если специально не ограничивать типы элементов.

Структурная директива может быть одна на один элемент, так как структурная создаёт экземпляр компонента и задаёт контекст

Речь о применении директивы к хостовому элементу.

Flutter, Svelte, Web Compose идиоматически ближе к React чем к Angular, на мой взгляд потому что подход React оказался проще и удобнее в плане эксплуатации.

Чтобы подвести итог этой статьи, я перечислю несколько проблем, с которыми я столкнулся в Angular:


  1. Он популярен в основном от того, что вокруг него много “шумихи” и потому что назван похоже на другой фреймворк, на котором уже написана тонна легаси (AngularJS). А ещё потому, что он распиарен как энтерпрайз решение, хотя для потребностей энтерпрайза он совершенно не оптимизирован.


  2. Он даёт слишком много свободы. Это приводит к фундаментальным ошибкам на ранней стадии разработки приложения, которые проявляются не сразу. И поведение по умолчанию этому только способствует (Привет, onPush).


  3. Использует не оправдано много памяти и не поддаётся оптимизации(not tree-shakable). Всё, что включено в ангуляровский модуль, попадёт в бандл. А в памяти будет создано 100500 объектов на каждый чих.


  4. Сложность Angular приложения растёт экспоненциально с ростом размера приложения и это затрудняет его обслуживание. Не говоря уж о том, что она изначально весьма завышена. Простейшее приложение из начала статьи — это уже 16 файлов преисполненных бойлерплейта.


  5. Нет ничего встроенного (например, рисования графиков, карт, таблиц и тд). По-этому вам нужно написать много кода, что бы это как-то компенсировать или использовать кучу сторонних библиотек.


  6. Обновление вашего приложения до последней версии Angular часто сопряжено с полным переписыванием этого самого приложения, кроме тех случаев, когда мажорная версия фреймворка поднимается просто так, без какого-либо развития внутри.


Замечательная, выдержанная в строгом стиле, концовка статьи.
Статья с громким названием, но по сути ни о чем.
Самый весомый плюс у Angular это то, что все проекты на нем похожи друг на друга. А вот в React не всегда…
А навостриться быстро клепать компоненты можно где угодно.
Или выбросить обеих и взятся за vue. И тот и дркгой продвигаются огромными корпорациями которым наплевать насколько сложно т трудоемко пользоватся их продуктами. У них вопрос ресурсов не стоит.

Желая досмотреть драку до конца, был избит обеими сторонами (с)

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

Кратко мои мысли в пользу react при прочтении: в большом проекте чистые (в идеале) функции и композиция оказываются проще в поддержке и лаконичнее, чем классы и наследование; структура проекта должна отражать предметную область, а не чьи-то представления о прекрасном, даже если это каноничный MVC; декораторы — это экспериментальная фича, которая может внезапно закончиться; jsx гибче обычных шаблонных движков в силу возможности использовать все доступные методы и структуры данных js прямо внутри «шаблона», а не надеяться, что автор языка шаблонов всё предусмотрел.

В размышления о том, что должно быть из коробки, а что — нет, я, пожалуй, влезать не буду, замечу только, что нет особых проблем установить нужные библиотеки. Проблемы «оно обновилось и всё сломало», к счастью, вылезают достаточно редко.

Самый большой плюс Angular — на нем сложнее говнокодить.

Самый большой минус Angular — очень плохая документация. Её даже просто "описанием API" только с натяжкой можно назвать.

Самый большой плюс Angular — на нем сложнее говнокодить.

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

Такая же иллюзия есть и с реаком, 99% проектов на связке React + Redux, так же старта уже говно, поэтому люди плодят говно дальше т.к. на фоне остального когда то, что они пишут особо не выделяется, плюс можно по тупому конечно оправдываться, ссылаясь на то, что в пример redux'a такой код и т.п., но конечно очень сложно подумать своей головой и понять, что код в промерах Redux'a как бы и есть говно. Но это очень сложно для обычных разработчиков.
На текущий момент я, если честно, вообще не вижу сферы применимости Redux. Простые локальные задачи решаются штатным state'ом, более сложные — хранилищами MobX.

Я не могу сказать, что мой метод применения MobX идеален, но пока серьёзных трудностей он мне не доставляет. Я разделяю проекты на несколько больших максимально независимых модулей (обычно они связаны с разными страницами приложения), объединённых лишь роутером, и под каждый создаю класс-синглтон для хранилища mobx, файл которого лежит внутри такого независимого модуля. Так я всегда знаю, к какому блоку состояний обращается тот или иной модуль и сохраняю их относительно независимыми между собой. Даже не знаю, как такого добиться в Redux.

Плюс подобного подхода в том, что я могу утащить модуль в другой проект банальным копированием папки и добавлением пути в роутер. Ну, и достаточно оторвать пару модулей, чтобы увидеть, наплодил ли программист излишней связности.
Все правильно, я тоже использую MobX ещё с 2016 года. Благо мазахизмом не страдаю и с Redux слез при первой же возможности и больше никогда к нему не возвращался и не вернусь ни за какие кавришки)

Да, mobx позволяет роуты-модули оформлять в отдельные независимые чанки, в то время как redux синглтон и загружает все сторы одновременно.


Модули лучше не линковать друг с другом и не плодить горизонтальных связей вообще, то есть при удалении каких-то страниц это никак не должно отразиться на остальных. Гардом может послужить автоматическое добавление-убирание модульных сторов в лайфцикле роутера, то есть даже при всем желании разработчик не сможет получить доступ к хранилищу другой страницы. Если уж надо переиспользовать — значит это состояние должно быть глобальным, а не модульным.

UFO just landed and posted this here

Складывается впечатление что автор не так хорошо знаком с реактом, ленивая загрузка и в реакте есть из под коробки, зачем упаминать её в статье?

"Теперь стало намного сложнее переиспользовать функциональность в моём приложении"

Именно за тем что функции и дают большУю гибкость в переиспользовании логики за счёт хуков, реакт и движется в эту сторону. Те кто работал с хуками знают как становится просто переместить логику из компонента за его пределы, при этом связывая её с другими хуками.

А вообще если человек знает что творит, он и на реакте, с учётом его большой свободы, создаст удобно-поддерживаемое приложение, так и дурачок на ангуляре, с учётом его строгости, запилит кошмар и ужас.

"если вы не используете какую-то часть Angular, она просто не попадёт в сборку!" чет орнул с этого, типа если в течение ни в одном из компонентов не импортировать какой-то другой компонент, то он попадает в сборку. Статья похожа на какую-то слабую попытку маркетолога

типа если в течение ни в одном из компонентов не импортировать какой-то другой компонент, то он попадает в сборку.


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

@NgModule({
  declarations: [FirstComponent],
  exports: [FirstComponent]
})
export class FirstComponentModule {
  
}

@NgModule({
  declarations: [SecondComponent],
  exports: [SecondComponent]
})
export class SecondComponentModule {
  
}


Пока не импортируешь FirstComponentModule — FirstComponent не попадет в сборку.

Если речь про то, что модуль установлен в проект (есть в папке нод модульс) но при этом его нигде не импортировать, то его не будет в сборке. Если речь о том, что модуль импортирован в какой-то компонент, но в данный момент на странице нет этого компонента то тут используются для этого чанки.

Зашёл почитать в комментариях холи-срач, но пока тихо.

Есть пара вопросов: вот это тэг <app-button></app-button>,

А на фига? Неужели все настолько "продвинулись", что забыли наивный js, с современными api HTML5?

И почему ангуляр это фреймворков, а реакт библиотека? Дайте тогда определение, а так это голимое IMHO.

Наверное по этот перл: "В отличие от виртуального DOM, в инкрементном DOM память расходуется при перерисовке дерева DOM, если только в нем произошли изменения (то есть были удалены или добавлены элементы). Кроме того, выделение памяти пропорционально размеру изменений" стоит оригинал перечитать, но в переводе уж0с какой-то. Хотя как сказать, дуралеи наверное без изменения DOM могут его перерисовать а-ля ... render(). Ну а концовка фразы перл ещё тот - неужели V8 движек даёт клиентскому скрипту залезать в память? Извините - не поверю; V8 прекрасно сам сжти сейчас управляется.

Расширял angular 9 проект, kaltura-cms ui, плюнул на это дело и начал инжектать реакт - за две недели наклепал больше чем за пол года. После angular 1 - angular 2+ это боль, которую невозможно описать, если коротко, то то что не надо расширили а то что надо убрали. Angular никогда уже не догонит реакт, и писать на нем свежатину неразумно.

Говоря с юмором — синтаксис при использовании структурных директив в ангуляре ужасен, ужаснее только синтаксис jsx.
Если вы хотите иметь группу разработчиков, которая может легко переходить от проекта к проекту, разрабатывать новые приложения, добавлять фичи с головокружительной скоростью и иметь дешёвых в обслуживании фронтэнд проектов, то садитесь на поезд под названием

react + mobx
По личным ощущениям: и на сегодняшний день и на продолжительное будущее — лучшая связка как с точки зрения разработчиков, так и с точки зрения бизнеса. (по соотношению стоимость разработки / поддержки / надёжности / производительности приложения / девелопмент-экспириенса / bus-фактора и т.д.)

Любая предметная область и извращённое бизнес-требование хорошо ложится, выразительно описывается и «кодируется» с помощью mobx-а.
При этом большая скорость изменений этих требований и большая неопределённость («энтропия») в них — также без проблем разруливается.

ужаснее только синтаксис jsx

что такое «синтаксис» jsx? и что в нём ужасного? чем он отличается от синтаксиса html? (не считая пары атрибутов типа класснейм, htmlFor ...)

Тоже никогда не понимал нападки на jsx — тот же html, но с компонентами (отличить можно по названию с большой буквы) и возможностью использовать js-переменные и код внутри фигурных скобок. Синтаксис изучается за день после изучения искусства верстки и вопросов/проблем возникает крайне мало, разве что по теме использования околохакового js-синтаксиса типа {isAllowed && <Component />}, так как тут нужно понимание одного из базовых принципов js — возвращаемое значение при использовании подобных операторов не булевое, то есть если isAllowed === 0, то будет выведено 0. Больше каких-то относительно сложных моментов и не припомню, но и это к jsx не особо относится.


В то время как в angular, svelte, vue продолжается практика придумывания мета-html языка с кастомным синтаксисом и параметрами, мета-js языка для перебора массивов в шаблонах и строковой логики в атрибутах. Я понимаю, что глаза замыливаются, когда работаешь с одной технологией, но я шаблонизаторов понаписал и поиспользовал массу, и jsx это крайне удобный вариант rich-html — быстроизучаемый, минималистичный, с отличной поддержкой в IDE, возможности ограничены только возможностями самого JS, никаких проблем с миграциями на новые версии или ожиданий доработки функционала. А та древовидная структура, в которую он легко может быть сконвертирован, удобна для рендеринга в DOM, так как иерархичность и названия элементов/тегов максимально приближены к нативным.


Но там комментатор явно пошутил про "ужасность jsx")

Из ужасного я вижу:


  • Недекларативность. Это капитально осложняет стат анализ. Например, построение конфигуратора, экстракция локализации, проверка переопределения стилей и пр.
  • Пуш семантика. Вложенное поддерево вычисляется безусловно, даже если оно завёрнуто в компонент, который показывает своё содержимое лишь иногда.
  • Неэффективность. Вызывается не напрямую код компонента, а абстрактная фабрика. Она является мегаморфной функцией и не может быть заинлайнена jit-компилятором.
  • Слабые возможности связывания. Двустороннее связывание делается через костыли с прокидыванием колбэка и его ручной обработкой и заворачиванием в хук.
  • Неконсистентность. Строки прокидываются одним синтаксисом. Все остальные типы — другим. Тут как раз подражание HTML не уместно.
  • Волшебные аттрибуты. Все эти ref, key и тп имеют особый смысл, но никак не отличаются синтаксически.
  • Костыли для комментариев. Набирать и читать их просто неудобно.
  • Много мусора в вёрстке. Лесенка из контекстов — довольно типичная ситуация.
  • Отсутствие ограничений. Неизбежно приводит к лапше в коде. Даже если код пишет не говнокодер, порой бывает, что некогда или лень делать правильно, а срезать угол ничего не мешает.
  • Шаблон сильно завязан на структуру данных. Опять же, никто не выносит получение всех вставляемых данных в отдельные функции. Особенно внутри итерирования.

Статическим анализом пользуюсь в виде TS и ESLint, они отлично поддерживают работу с jsx. Конфигураторы форм, хранилище локализаций — это не забота языка описания интерфейса, но преобразование из js-структур в jsx-разметку происходит через удобный js-код, а не встроенные (в лучшем случае) в шаблонизатор кастомные атрибуты и значения. Переопределение стилей в императивном формате style={{ width: 100 }} неизбежно во многих кейсах, и ничего страшного в этом нет. Почему все эти разноплановые темы вы объединили в один пункт "недекларативность" мне не понятно, и какие "декларативные" шаблонизаторы могут все это решить лучше, тоже не знаю.


Абстрактная мегаморфная фабрика это про дерево, состоящее из React.createElement(type, [props], [...children])? Да, подход с shadow dom не так хорош по перфомансу, но его хватает для 95% SPA, в остальных, где требуется максимальная производительность и минимальное количество итогового кода, подойдут другие инструменты, а не Реакт, но будут значительно сложнее в поддержке. Я бы не стал это записывать в существенный недостаток, так как у других подобных популярных инструментов производительность сопоставима (кроме Svelte).


В Реакте в целом используется концепт однонаправленного потока данных, это вполне удобно и с помощью mobx максимально оптимизируется: <Button onClick={() => store.counter++}. Соответственно, все компоненты, которые подписаны на store.counter обновятся, и проще вариант придумать сложно. Для сохранения равенства по ссылкам и переиспользуемости этой функции ее действительно можно вынести или в хук, или в экшен, или в метод класса — в зависимости от того, как построена архитектура. Но и инлайновый вариант, опять же с кейсом mobx, крайне не значительно ухудшит перфоманс. Двусторонняя связка между компонентами в данном случае производится через общий контекст — общий объект с источником правды, что позволяет максимально сократить горизонтальные связи, и это в моем понимании безусловный плюс.


Неконсистентность в передаче параметров решается с помощью ESLint (jsx-curly-brace-presence), так же как и делается запрет на строковые ref ('react/no-string-refs') для того, чтобы "отличать их семантически от key", и проверка установленного key ('react/jsx-key'). В этом плане статический анализ работает прекрасно, и три этих относительно спорных момента регулируются практиками, принятыми в проекте. Волшебный смысл есть только в key, ref это просто получение ссылки на элемент. Тут соглашусь, что наличие key является недостатком. Как и довольно страшные комментарии.


По поводу мусора в верстке — это не вина jsx как шаблонизатора, а кривизна рук. Многие тащат роутинг (!) в разметку, действительно выстраивают дополнительный иерархичный слой из контекстов, опять же в разметке. Но по большей части результирующий html и jsx совпадают, в отличие от большинства других шаблонизаторов. И в них тоже можно намусорить.


Ограничения устанавливаются с помощью TS, ESLint и стандартов кодирования, проверяются с помощью pre-commit hooks, CI, code review. Написать лапшу и "срезать угол" можно в любой системе, тут jsx наравне со всеми, и не его это дело — устанавливать практики. То, что в других шаблонизаторах/фреймворках могут присутствовать уже сконфигурированные анализаторы кода и четко описанные стандарты кодирования в общей документации — это можно рассматривать и как плюс, и как минус. Есть мнение, что "ваша демократия и самоуправление — это хаос, а настоящий порядок — когда диктатура и все беспрекословно в страхе подчиняются", но я сторонник баланса, когда ограничения есть, но они задаются контекстуально, меняются со временем и подстраиваются под развитие всей системы.


Изменение структуры данных в любом случае потребует изменений в рендеринге. Согласен, что обилие js-вставок в разметке это скорее антипаттерн, а итерирование лучше делать через дочерний компонент для оптимизации. К самому jsx это, опять же, не имеет отношения.


В сухом остатке я согласен с критикой по поводу лишнего key, неудобного комментирования, неидеальной реализации механизма синхронизации dom и jsx (но это не к шаблонизатору). Остальное решается либо стандартными сторонними инструментами, либо есть отличные альтернативы по паттернам, в случае недостающего функционала.

Sign up to leave a comment.

Articles