Как стать автором
Обновить
44
-3
Дмитрий Казаков @DmitryKazakov8

Front-end архитектор

Отправить сообщение

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


При необходимости (после завершения куска логики) можно вручную вызвать проверку типов, и обязательно — на pre-commit+CI. Это максимально удобно, и скорость инкрементальной сборки на современных сборщиках почти мгновенная.


Еще в Бабеле запилили хороший кеш, который тоже значительно сокращает время сборки (перешел на него с happypack/thread-loader и стало быстрей, чем параллельная сборка в нескольких процессах).


А про один AST на разные инструменты, конечно, согласен, но вряд ли в обозримом будущем смогут договориться Eslint+TS+babel+IDE на единый всем подходящий формат, так как это будет блочить развитие и оптимизацию каждого инструмента в отдельности.

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


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


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

Эта эпоха никуда не делась, просто "ушла в тень". Но для даркнета не подойдет такая либа, т.к. завязана на js

"Я сам не слышал, но мне так сказали". Я работал только с первым ангуляром, про ломающие обновления слышал от коллег, поэтому аргументированно не смогу ответить.

Хотел бы я посмотреть на код автора этой статьи… Все равно ему придется писать и механизм рендеринга в DOM, и механизм обновления данных патчами (т.к. переписывание innerHTML собьет listeners), и жизненный цикл компонентов (т.к. нужно где-то очищать listeners и timeouts), и механизм обработки пользовательских событий (onclick="window.myFunc" не подойдет из-за засорения глобальной области видимости и недоступности локального контекста данных), и экстракт состояния, отрендеренного на сервере, из разметки, и много чего еще. Не собирается же он бэкендовым twig рендерить модалки, графики, селекты, валидации форм и т.п., чтобы при каждом клике на элемент страница перезагружалась?


Насчет недостатков Angular я скорее согласен с его ломающими обновлениями и обилием встроенных библиотек, но про "другие модные библиотеки" он так зря.


"Святой Грааль написания кода, любого кода, заключается в простоте" — тут как раз если ты знаком с фреймворком, то при переходе в новый проект очень быстро можешь разобраться и начать писать код. В общем, выглядит, как жалобы верстальщика лендингов с каруселями на jQuery, который посмотрел на серьезные проекты и подумал — нет, я слишком стар для такого, лучше буду хейтить все подряд.

Декорирование — древний концепт, и нападок на него как на паттерн я не понимаю. Это оборачивание функции в новую функцию, которая делает что-то дополнительное, а затем вызывает оригинал [+ что-то дополнительное]. Если нападки на синтаксис с "собачкой" над свойствами класса — так это можно воспринимать как сахар, который стабильно корректно транспайлится бабелем уже много лет и не вызывает проблем.


Пожалуй, отказ от этого сахара может быть только по одной более-менее разумной причине — желании сохранять итоговый код как можно ближе по синтаксису к исходному. Но тогда исходный надо писать на ES2015 — на Хабре есть такие любители, даже если профессионалы, но все равно встречаются нечасто. А тут вот в статье еще один — никаких классов (для сторов и для компонентов), никаких типов, никакого Prettier с форматированием с отступами. Удивительно, что Proxy со скрипом, но приняты к использованию...

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


Независимость релизов от core-обертки иногда благо, иногда — трагедия. Тоже были случаи, когда в core были breaking changes и надо было все микрофронты обновить (и не раз такие случаи), и если бы в одной репе это выглядело бы в основном как "пробежался автозаменой — проверил — выложил", то при многих командах выглядело как "фриз core -> ждешь неделю апдейта от 3 команд -> фриз этих 3 команд -> ждешь 2 недели остальные команды либо лезешь сам разбираться в их зоопарке -> общий тест -> еще несколько правок, пока все команды зафрижены -> релиз". Сколько тут ресурсов и времени разработчиков терялось — не сосчитать, а опыт взят из богатой конторы с большим количеством фронтов. Ошибка была только одна — решили делать микрофронтенды. Таких историй у меня выше крыши, но статьи по ним не пишут)


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


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

Редко появляются технические статьи из фронтового энтерпрайза, интересно почитать. Первые две схемы действительно полны недостатков, третья — выглядит удобнее, но возиться с ее настройкой ввиду новизны приходится немало. Насчет стрелочных функций в IE, кстати, так как модули — отдельно собранные файлы, то при их загрузке код можно выполнить в try-catch с отслеживанием превышения времени выполнения в секунды 3 (для защиты от бесконечных циклов) и логировать случаи жестких ошибок или некорректного синтаксиса. Если уж совсем независимые команды делают, то это дополнит защиту ErrorBoundary.


В целом про микрофронты можно написать много нехорошего, и приведенные проблемы здесь — вершина айсберга, под каждой строчкой в тексте виден опыт страданий, проб и ошибок, а то, что получилось в итоге все равно имеет большую сложность, несмотря на ужимание концепта микрофронтов до "монобиблиотека + моносборщик + монофреймворк", просто с разнесением по нескольким репозиториям с условно-независимым циклом поставок. Больно было читать про сотни граблей и упорную борьбу с недостатками каждого подхода, чтобы всего лишь распараллелить работу команд, использующих одинаковый стек.


При этом в монорепе:


"Команды разрабатывают модули изолированно и не влияют друг на друга" — если страницы сделаны через вебпаковые асинхронные импорты, то проблемы нет. В каждой папке страницы может быть много модульных сторов, экшенов, компонентов, роутов, апи-запросов — и для разработки сложных страниц не нужно лезть в окружающий код, при необходимости изоляцию (запрет глобальных импортов) можно включить в eslint-правила. Не так уж сложно проконтролировать то, что "команда не может внести изменения в архитектуру системы" — а с точки зрения безопасности, если захотят, то и через iframe, npm или module federation встроят любой код типа картинки, делающей гет-запрос с шифрованным auth-токеном и базой юзеров. Микрофронты тут никак не защищают, и вообще встраивать код быстро меняющихся чужих команд которые пишут как хотят внутри своих реп — никак не по энтерпрайзу.


"Изолированность позволяет каждой команде работать в привычном режиме" — в монорепе вполне можно сделать удобные релизные ветки и менеджерить общие релизы в мастер. Есть проблема с откатами, но с микрофронтами там тоже все непросто.


"Команды придерживаются отличающихся практик и требований к написанию" — если так уж нужно, можно отключить для определенных папок-страниц проверки ESLint и даже TS, если некая аутсорс команда не хочет автоформатирования, типизации, проверки ошибок в рантайме и ей обязательно нужны табы вместо пробелов для отступов и длинные строки инструкций.


"Бизнес-процессы в некоторых модулях очень сложны" — никак не связано с микрофронтендами, почему написано в списке причин их заводить — непонятно.


Итого — вместо менеджеринга фиче- и релизных веток в монорепе было решено пойти во все тяжкие, потратив большое количество ресурсов компании и получив все возможные "болячки" микрофронтов, о которых уже много было написано. При этом при выделении в монорепе просто изолированных папок-модулей и при асинхронной их подгрузке, не было бы проблем с кешем, полифиллингом (в микрофронтах полифиллы дублируются, т.к. не весь код прогоняется через корневой бандлер), передачей глобальных данных (сторов) и методов (контекст, пропсы), расхождением или дублированием версий uikit и других библиотек от модуля к модулю, дублированием, выделением общих сложных компонентов (типа панели выбора сотрудника), настройкой ci/cd, локальной подгрузкой актуальных модулей (т.к. есть общий master), возней с версионированием и откатом npm-пакетов, типизацией, таких проблем с безопасностью этих "черных ящиков"...


Не проще ли выбрать пару понятных проблем монореп — необходимости аккуратного менеджеринга веток разными командами и оптимизации первичной сборки и пересборки, вместо всего набора этих и многих других проблем (особенно если вдруг станут не моностековые микрофронты)?

Хотя я не согласен с MaZaAa, что код читается слева-направо, в вашем continueBtnActive много недостатков.


Во-первых, использовать специальные конструкции языка — значит ухудшать читаемость и скорость восприятия для разработчиков в целом. Если вы видели конструкции Elixsir или Ruby, тогда не нужно объяснять, если не видели — то Boolean() всегда предпочтительнее !!. Описание логики "не на языке, а с помощью языка" — одно из ключевых требований к синьорскому коду, по крайней мере в проектах с долгосрочной поддержкой.


Во-вторых, приведение типов в JS — очень сомнительное "удовольствие", так как часто результат будет не тем, что ожидается. TS решает эту проблему только частично, оставляя пространство для логических багов. Поэтому чистое сравнение this.title.value.trim() === '' выглядит намного лучше.


В-третьих, работа с операторами && и || требует хорошего знания работы языка, так как сравнение производится как boolean, а результат возвращается как значение, соответственно 0 && 1 выведет 0, а не false. В конкретном коде это не играет роли, но такие ошибки предикции распространены очень сильно. И даже в примере — чуть удлинится код, и предсказать возвращаемое значение будет сложно как путем визуального анализа, так и статическим анализом, либо результатом выполнения.


Что касается темы "кнопки не должны сейчас срабатывать" — в любом случае, стейт машины запущены или отдельные функции, придется описывать эту логику отдельно. Если, конечно, в XState нет проверки disabled={someStateMachine.isTransitioning}, так как добрая половина логики в приложениях — асинхронная. С MobX это описывается очень просто.


Несмотря на быковатый русский у MaZaAa во всем треде я не вижу его явных ошибок в суждениях. В концепте change-reaction не может быть недостатков, есть только непродуманное ее использование.

Если в проекте используется Webpack, то он при билде строит граф зависимостей, который можно отобразить в любом формате. Например, такой миниатюрный плагин позволяет описывать в .dot типа


digraph sources {
        rankdir=LR;
    "node_modules/@sentry/hub/esm/index.js" -> "node_modules/@sentry/hub/esm/session.js";
    "node_modules/@sentry/hub/esm/index.js" -> "node_modules/@sentry/hub/esm/scope.js";
    "node_modules/@sentry/hub/esm/index.js" -> "node_modules/@sentry/hub/esm/hub.js";
    "src/components/Footer/Footer.tsx" -> "src/components/Footer/Footer.scss";
    "src/components/Header/Header.tsx" -> "src/components/Header/Header.scss";
    "src/assets/icons.ts" -> "src/assets/icons/arrow-left-bold.svg";
    "src/assets/icons.ts" -> "src/assets/icons/arrow-left.svg";
    "src/assets/icons.ts" -> "src/assets/icons/arrow-right-bold.svg";
    "src/assets/icons.ts" -> "src/assets/icons/arrow-right.svg";
    "src/assets/icons.ts" -> "src/assets/icons/auth.svg";
}

который можно отобразить графически в одном из многочисленных онлайн просмотрщиков, либо в плагине к IDE (для WebStorm тот же dotplugin). Плюс в том, что можно самостоятельно выбирать итоговый формат, фильтровать, конкатенировать, включать в git для просмотра из онлайн-утилит, делать статический анализ зависимостей по сгенерированному графу в гит-хуках или CI. Минус в том, что учитываются только импорты, а не динамически используемые зависимости (через IoC, Context), поэтому для полноценной демонстрации архитектуры не подходит, но полноценный граф по реальным динамическим usages статическим анализом сгенерировать сложно.


Как-то медитировал над задачей "найти все использования параметров хранилищ в компонентах", но для этого нужен стейт, отслеживающий использование параметров (как MobX) и прогон полного автоматизированного регресса сайта. Хотя это вполне реально сделать, на практике фактически бесполезно — только для проверки, правильно ли используется архитектура в проекте.

SSR — вынужденная мера, и не стоит тут фронтендеров винить. Он решает 2 проблемы — индексация поисковиками и быстрая поставка готовой разметки. Первая проблема может решиться улучшением поисковых движков, вторая — либо уменьшением размера JS кода, либо статичным пререндеренгом, либо динамическим (SSR). Последний способ решает и первую и вторую проблемы, хотя требует довольно большой возни. Так что да, не мы такие — жизнь такая.


В тексте статьи у вас увидел parallel-webpack, сейчас бы я не стал его рекомендовать — там накопилось довольно много багов и репозиторий заброшен, проще написать свое решение типа такого webpack-parallel-simple

"Тут нет лишнего перерендера" факт, но для чего лишний проп? Если для виртуального рендеринга компонента в тестах, то подойдет и "неспортивный" мок контекста. В остальных случаях в этот компонент не будет передаваться другой router, кроме как глобальный, поэтому можно упростить интерфейс.


Я много раз пробовал схему подключения конкретных сторов через useContext или inject в декораторах (если классовый подход), но это исключительно лишний код — не было ни одного случая, когда это принесло бы пользу, кроме спорного "явного указания зависимостей". Так что почему "должны"? Может, есть специфический кейс, о котором я не знаю? В чем "слабое зацепление" в виде передачи многих одинаковых пропсов в компоненты или многочисленных useContext дает больше "запаса прочности"?


Пример селекторов, независимых от стора, привел в const getters.

Как-то много всего перемешано в статье… "Модное — не лучшее", но при этом используется очень многословный Redux, будто бы это "проверенная временем классика"… Но подобная архитектура абсолютно не прошла проверку временем, а привела к колоссально раздутым и тормозящим проектам, так как чтобы написать высокопроизводительное приложение нужно очень, очень много кода и ручных оптимизаций.


По поводу вложенности хуков — соглашусь, с первого взгляда даже видна следующая ошибка:


Notification вызывает на каждый рендер хук useBestFriendNotifier -> 
useBestFriendNotifier вызывает на каждый вызов useFriendStatus -> 
useFriendStatus на каждый вызов создает новую функцию handleStatusChange -> 
return () => ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange) не может отписаться от функции handleStatusChange, так как в componentWillUnmount она не будет равна по ссылке той, на которую он подписался.

Вариант решения — обернуть handleStatusChange в useCallback. Не о многом ли заставляет задуматься этот пример? Может, функциональная композиция в таком виде — скорее боль и причина возникновения сложно уловимых багов? В реальности видел большое количество подобных проблем, где приходилось копаться в композиции хуков, корректировать массивы данных, при которых он будет обновляться, глубоко продумывать равенство по ссылкам. На Хабре уже много обсуждали недостатки хуков (которых огромное количество) и то, что писать некорректный код на них проще простого, а корректный становится намного более перегруженным, чем в классовых компонентах. Учитывая все недостатки хуков, больше не использую их в энтерпрайзе и, соответственно, никаких новых сложностей в проекты не добавляется.


К слову про Редакс — если вам так он нравится (видимо из-за чистых функций и иммутабельности), то очень удивительно видеть подход, когда в хуках прямо рядом вызываются сайд-эффекты, подписки, наверняка еще и обработка пользовательских событий, но по какой-то причине это вы не описываете в выводах против хуков, хотя код прямо очень контрастный из-за смешанных концепций.

"неплохо бы думать как о потенциально переиспользуемом" !== "внятный минималистичный API"


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


render() {
  const { store } = this.context;

  return (
    <>
     <Component1 pageTitle={store.routing.pageTitle} />
     <Component2 pageTitle={store.routing.pageTitle} />
    </>
  );
}

В этом кейсе:


  • родительский компонент обновляется, когда не должен из-за изменения pageTitle
  • в дочерних компонентах нужно указывать propTypes
  • распространен кейс, когда этим дочерним компонентам не нужен pageTitle, а он нужен еще глубже. Предыдущие проблемы умножаются, приложение тормозит, длинные цепочки props drilling…
    А когда такой параметр не один, а много? Очевидно, что увеличивается и связка компонентов, и осложняется рефакторинг (вполне можно "забыть" убрать у одного из родителей этот параметр), и код становится замусоренным.

С глобальным компонент любой вложенности может брать store.routing.pageTitle из контекста. Мокается не сложнее, чем <StoreContext.Provider value={{ routing: { pageTitle: 'sample' }}}>. Обновляются компоненты точечно, пропсы не раздуваются, рефакторинг одной строчкой. Какие бы принципы ООП это ни нарушало, простота и поддерживаемость — то, для чего все принципы программирования создаются, и если в React+MobX какие-то неэффективны, не следует вслепую их применять.


Компонент, который подключается к глобальному стейту не "самостоятельно резолвит свои зависимости", а использует переданные, но только не через раздутые propTypes, а через контекст. Нет ничего зазорного, чтобы передавать данные через этот интерфейс — пусть он не настолько явный, но очень удобный для реактивных хранилищ.


По поводу классового DI — это тоже применение паттернов, которые не оправданы реальным использованием. Если классы-хранилища не будут знать друг о друге и изолированы, но из двух классов можно сделать выборку данных и реактивно эту выборку (селектор) обновлять — сколько тут принципов нарушается? Кажется, меньше, чем с DI. Как понимаю, основной консерн у вас в доставшемся от Редакса концепте "надо давать в компоненты как можно меньше данных, а не стор целиком" — это травма, вызванная иммутабельностью и неточечной реактивностью, уже можно забыть об этом. Если мы дадим в компонент весь стор, а он использует из него один или сто параметров — с MobX не важно, но это максимально удобно.

В классовом DI бойлерплейта много по сравнению с отдельными экшенами и селекторами. Про явность зависимостей тоже некорректно, сравните:


class StoreRouting {
  storeUser?: StoreUser;
  pageName = 'Profile';

  constructor(storeUser: StoreUser) { this.storeUser = storUser; }

  pageTitle() {
    return this.pageName + ' ' + this.storeUser.userName;
  }
}
// и при создании инстанса надо не забыть о пробросе storeUser и порядке создания констант, при этом ряд инджектов не будет доступен

***

const getters = {
  pageTitle() {
    return globalStore.routing.pageName + ' ' + globalStore.user.userName;
  }
}

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


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

ООП с DI на MobX - это не то, чтобы эффективный подход... Он лучше любого на Redux, но в целом недостатков много - классовый DI делает хранилища запутанными и несемантичными, привносит дополнительный бойлерплейт, становится сложно контролировать циклические зависимости. Экшены часто 1) семантически не подходят стору; 2) используют параметры из нескольких сторов; 3) меняют параметры в нескольких сторах. Первый и второй пункты подходят и для селекторов.

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

Статическим анализом пользуюсь в виде 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 (но это не к шаблонизатору). Остальное решается либо стандартными сторонними инструментами, либо есть отличные альтернативы по паттернам, в случае недостающего функционала.

Если под "связями между моделями" подразумевается, что используется классовый DI и в один класс хранилища прокидываются еще несколько, а в те, в свою очередь, еще несколько — это действительно предвестник полной запутанности и неподдерживаемости. Такая архитектура крайне специфична и подойдет возможно только определенным браузерным играм, но видел ее применение и в обычных SPA, что действительно расстраивает. Только не из-за mobx, а из-за классового DI и полного размазывания ответственности хранилищ под соусом "изолирования и явного указания зависимостей", что прямо противоположно результату.


Для выведения значений сразу из нескольких семантических сторов нужен слой getters/selectors с реактивными computed-значениями, для изменения данных сразу в нескольких сторах — слой глобальных action-модификаторов состояния. Только времени разработчикам на то, чтобы продумать это все, как правило, не дают — от фронтендеров бизнес ждет быстрый видимый результат, а не продуманную архитектуру, удобную для разработки и минимизирующую количество ошибок. Вообще часто видел очень запущенный код с громадным количеством функционала, добрая половина из которого не работала и про которую бизнес даже забыл, не удосужившись вести полноценную базу знаний по продукту. И в таком виде приложение существовало долго и приносило доход, что и было целью для руководителей компании, в то время как разработчики удерживались методами, не связанными со "стремлением к саморазвитию, внедрением новых технологий, созданием стабильных архитектур". К чему я это все — к тому, что проект проекту рознь, и там, где достаточно времени уделяется качеству, можно практически на любом инструменте создать грамотную схему работы. Но с mobx получится на порядок лучше, чем на любом другом, и практически без бойлерплейта.

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


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


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

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


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

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность