Обычная очередная внутрикорпоративная библиотека, которая интегрируется в корпоративный стек этой компании, частично выпущенная в опенсорс. Вспоминается тот же БЭМ, который подходил если для него уже есть большая унифицированная инфраструктура, а как частичный выпуск в опенсорс был хуже тех же CSS Modules, так как решал проблему изоляции стилей многословно и ограниченно (все равно возможны глобальные пересечения).
По мере развития css в котором теперь и переменные, и функции, и обсуждается внедрение mixins эти подходы с css-in-js смотрятся настолько же устаревшими и неактуальными, но если в конкретной компании под StyleX все подготовлено и унифицировано - то им проще его использовать. Это вовсе не для "долгосрочной поддерживаемости" сторонних проектов, которые будут использовать эту библиотеку. Думаю цель выпуска в опенсорс - использование ресурсов бесплатных разработчиков-контрибьюторов и бесплатного тестирования, как это часто и бывает.
Тут не наш профиль) Статью про блоги / контентные сайты тегнули Реактом зачем-то. И так понятно, что для этих задач даже Wordpress тяжеловат и лучше что-то совсем простое на статике делать с минимальным шаблонизатором и нативным js для кнопки "лайкнуть". И уж точно тут не нужен Next или другой мета-фреймворк.
Люди ругаются, что для простейших задач кто-то тянет инструменты уровня "из пушки по воробьям", и это оправданно. А для веб-приложений действительно нужен полноценный стек. Одно смущает - что кто-то этого действительно не понимает и ставит минусы, думаю тут просто недопонимание.
Ну получилась самая базовая реализация, до production-ready еще пара десятков итераций и переосмысления нужна.
Много бойлерплейта, создание через классы new FormGroup(), new FormContol() - это не сериализуется в json (значит не будет удобного SSR), синтаксис - плоские аргументы ('', [required()]) с "магическим маппингом что первый аргумент - значение, второй - массив валидаторов". Это не расширяется, а валидаторы в массиве - очень неудобно в реальности, лучше объектом.
Вывод в UI недекларативный. Да и в целом может понадобиться много параметров для инпутов - isFocused, isDisabled, options (для селектов), formatters, кастомное поведение валидации на onBlur либо на onSubmit, показ лоадеров и предзагрузка данных, и т.п. То есть лучше всегда оставлять конфиг формы гибким и расширяемым для пользователей библиотеки.
В общем, ничего пока что интересного по сравнению с подходом отсюда .
Согласен, в статье хорошо разобрана одна из сфер, где пригодится BFF. В целом "классическое" его назначение - это SSR / SSG, если проекты клиентские (не внутренние админки в закрытых сетях), контроль CSP (т.к. фронтендерам часто удобнее поддерживать именно там динамические политики, чем лезть в nginx или другой прокси) и изоморфный контроль прав. Вот последнее - это интересная тема, разверну.
Возьмем как раз админку в закрытой сети. Бэк отвечает в целом за авторизацию, плюс есть роли / фича-тогглы и например 100 ручек. Нужна мощная система контроля доступа (например, показ определенного раздела только если есть фича-тоггл, а также гранулярный контроль возможности редактировать/обновлять/удалять/смотреть подразделы). Фронт в любом случае будет это делать if-else для показа элементов, то есть будет обладать 100% информации о том, что где кому доступно. А бэк обладает ограниченной информацией, т.к. часто нужно строить граф зависимостей фича-тогглов (например, раздел "продукты" тянет 10 ручек словарей, ручку авто-анализа, вывода таблицы, редактирования-удаления, стейт-машин и еще несколько смежных). Фронт уже имеет этот граф, и может безошибочно сказать "вот эти 10 ручек должны быть доступны, если включен фича-тоггл просмотр продуктов".
Поэтому в BFF просто можно взять готовый граф из фронта, и отдавать 403 сразу из node.js вместо проксирования в бэк. А поддерживать это все и синхронизировать в реальном бэке - "черная дыра" по ресурсам и потенциальным багам + замедление релизов и усложнение тестирования. Тут BFF для гранулярного контроля выступает отличным ускорителем разработки и достаточной защитой от несанкционированного доступа, тем более что при продуманной архитектуре нужно ровно 0 изменений в BFF - при изменении логики все подтянется автоматически.
Есть и множество более частных применений (например вебсокет-прослойка или SSE), и описанная в статье "подготовка и оптимизация данных под конкретного потребителя" - тоже очень валидный кейс. Особенно BFF помогает, когда узкое место - это количество/качество бэк-команды и она не успевает за фронтом, тогда можно временно "набросать для показа" на node (либо полноценно с интеграцией базы, либо на моках). Бизнес будет доволен, инвестиции будут получены, полноценный бэк - подтянется в своем ритме.
Будет интересно посмотреть, как у вас будут реализованы формы и сравнить. Я обычно беру такой подход (универсальный для React, Solid, Vue, просто движок реактивности разный под капотом - синтаксис полностью одинаковый).
form здесь - независимый реактивный объект, в терминологии Mobx - observable. Расширяемый, полностью типизированный, можно создавать копии или использовать 1 инстанс если нужна синхронизация, SSR из коробки (в том числе с уже выведенными в интерфейс ошибками).
Разметка - декларативная, сами компоненты инпутов просто маппятся по type из любой UI-библиотеки с простой интеграцией (по сути в onChange инпуты меняют соответствующий value).
Соответственно, работают любые реактивные паттерны - computed геттеры (если нужно собрать данные из нескольких полей и по-особому вывести), autorun / reaction если нужна логика взаимосвязанности (например - при изменении одного поля изменить значение в другом или отфильтровать options / перевести в disabled, а также для взаимодействия нескольких форм).
В целом, подходит для сложнейших форм и не натыкался на ограничения. Единственное, что не всем понравится - data-driven (отсутствие каких-либо onChange колбэков). Если надо отреагировать на установку фокуса в инпуте - будет reaction(() => insuranceStartDate.isFocused, callback), а это часто сложнее воспринимается, чем onFocus, и требует dispose при анмаунте компонента. Но для использующих реактивные подходы - привычно.
Вот как раз что я и ожидал - ответ от ИИ, полностью игнорирующий описанный мной концепт, и рекламирующий библиотеку)
i18next, next-intl или vue-i18n - неоптимизированные библиотеки с неэффективным использованием, тут соглашусь, но я же описал подход, никак с ними не связанный. Файлы message.ts - модульные, а общий json нужен только для переводчиков и удобной выгрузки в системы локализации. В коде его вполне можно разбивать на чанки хоть под каждый модуль, хоть под скоуп модулей (например для страницы src/pages/intro брать все тексты из json с этим префиксом). Это гибкость, универсальность и минимальный бойлерплейт.
Конфликты при слиянии веток здесь возможны только если параллельно работают над одним и тем же messages.ts - и точно такие же конфликты будут в Intlayer, так как он тоже использует модульные файлы с локализацией (но вроде без сборки их в большой json). Но это "слабые" конфликты, которые легко решаются автоматически, т.к. все ключи - уникальные.
Довольно часто вижу в ленте статьи по Intlayer - вы стараетесь его популяризировать, но не понимаю практической применимости. Обычно переводы в проектах делаются проще и эффективнее.
Посмотрим в контексте "ретроспективного внедрения интернационализации" + React. На данный момент все тексты захардкожены в компонентах, бизнес ставит задачу - перевести на несколько языков.
1.Нужно вынести тексты в объекты, это прекрасно типизируется TS и есть немало инструментов, позволяющих идентифицировать "неиспользуемые ключи в объекте", то есть масштабируемость - хорошая.
// было
function Component() {
return <div>Hello</div>
}
// стало
const messages = {
hello: 'Hello'
}
function Component() {
return <div>{messages.hello}</div>
}
2.Вынести объект messages в отдельные файлы для переиспользуемости и задать автогенерацию уникальных ключей
__dirname здесь - простейший уникальный для файла идентификатор, многие бандлеры умеют с ним работать "из коробки". Но можно использовать любой, прокидывая бандлером.
3.Добавить возможность подключить шаблонизатор для плюрализации, работы с датами, падежами и т.п., готовых в js-экосистеме масса. Достаточно сделать функцию ln, а движок подбирать по нуждам проекта либо делегировать это сторонней системе вроде Localise.
function Component() {
return <div>{ln(messages.hello)}</div>
}
Функция ln может под капотом иметь разную реализацию (например, возвращать реакт-компонент) или быть реактивной на сигналах (сменилась локализация - все функции перевызвались, обновив тексты в приложении), это все делается парой строк.
4.Собрать все тексты в json-файлы и встроить генерацию в бандлер перед сборкой и в watch-режиме
Для многих проектов подойдет автопереводчик с ручной правкой, для более сложных - этот json можно автоматизированно выгружать в облачную систему для переводчиков, которые 1 кнопкой пушат его с переводами обратно в репозиторий.
Собственно, все. Это база, которая и так есть в большинстве проектов, и может очень гибко настраиваться под стек, в том числе делая отдельные json-чанки под компонент (чтобы не все локализации грузились сразу). С ИИ этапы 1 и 2 делаются за полчаса, 3 и 4 пишутся за день под конкретные технологии и требования в проекте.
Итог - задача выполнена, при добавлении/удалении ключей в любом модульном messages.ts обновляется JSON, выгружается в систему переводов, и для изменения переводов не нужны разработчики - переводчики могут менять тексты сами, не влияя на код проекта.
А каждый раз, когда захожу на документацию Intlayer, не понимаю зачем столько бойлерплейта и сложностей, зачем вручную задавать ключи с дубляжом для каждого языка; писать переводы прямо в messages вместо того, чтобы отдавать json переводчикам или в автоперевод (поэкономьте время разработчиков); привязываться к встроенным механизмам шаблонизации (когда часто это должно быть частью локализационной системы типа облачного Localise). И много еще другого, что мне кажется крайне неэффективным, и видя это закрываю вкладку с документацией.
Скорее всего, пишу это "в пустоту" - половина ваших комментариев на Хабре ИИ-нейрослоп, и все для рекламы, но слишком уж часто эта поделка попадает в ленту.
К более узкой экспертной специализации так или иначе многие приходят. Если тебя не "ведут за руку" на курсах с трудоустройством, а учишься сам - то в любом случае пробуешь себя в разных сферах.
Раньше вот были профессии "вебмастер" или "веб-технолог" - это и дизайн, и верстка, и фронт, и бэк на CMS, и настройка серверов, и обслуживание / мониторинг, и общение с бизнесом для составления ТЗ, и даже наполнение контентом. Да, глубоко погружаться в каждую область не требовалось - зато позволяло выбрать то, что интересно.
Я тоже выбрал фронт - и это огромная область, к которой сам раньше относился как к "формошлепству", но оказалось что требуется десяток лет, чтобы нормально сделать архитектуру под форму на 50 взаимосвязанных полей с 15 компонентами (range, daterange, tree select) с десятком модалок на заведение новых сущностей тоже с громадными формами, SSR этого всего с асинхронной подгрузкой данных для каждого селекта, параллельной работой по сокету нескольких сотрудников, сохранением недозаполненных форм в Local Storage, фича-тогглами и ролями какой кому показывать инпут а какой отключать в disabled... Это маленькая задача, каких тысячи в реальных проектах среднего (даже не крупного) размера на 2 дня, и нужна мощнейшая автофайлогенерация, продуманные полностью типизированные библиотеки и длинные надежные цепочки отказоустойчивости с мониторингом (иногда - в реальном времени).
"Универсал" такое не делает и поэтому компании ищут именно специализированных фронтендеров, это вполне логично, причем на определенном стеке, потому что под каждый нужен свой набор библиотек и архитектурных подходов. Зато если стек выбран популярный, а не нишевый - то хорошую работу найти намного проще, чем универсалу. Да, пришлось весь нерелевантный опыт "сложить на полку", но все равно широта знаний помогает и можно для интереса брать задачи на фулстак например.
Я думаю, что "наш код" никогда не был чем-то особо секретным. Я фронтендер, весь js код в любом случае должен быть прочитан браузером, а техники "защиты" в виде обфускации не работают, т.к. ключ декодирования все равно должен быть доступен браузеру, а значит - всем кто заходит на сайт. Поэтому по большей части используют просто минификацию, а провести реверс-инжениринг и сделать клон - не такая уж сложная задача и для человека (я много так не-опенсорс библиотек "форкал" и дорабатывал еще лет 15 назад). Для ИИ просто это все намного проще.
По бэку сложнее, но опять же все публичные контракты можно вытянуть и вывести логику через эксперименты в Postman, и запрограммировать на любом удобном языке. Главная ценность в ИТ с давних пор - не код и логика, а данные и привязка аудитории. Так, никому не нужен клон Telegram - его код спокойно лежит в опенсорсе, потому что у клона не будет аудитории. А у клона Хабра - не будет данных и опять же лояльности посетителей.
Так что ИИ не приносит тут ничего нового, если не давать доступ к прод-базе, и уж тем более не сможет эффективно привлекать аудиторию, потому что не имеет мышления людей и понятий о "красоте" или "удобстве".
В статье очень точно описаны и мои наблюдения. Главное в работе инженера-программиста - это постоянно развиваться, продумывать наиболее оптимальные паттерны, подходящие конкретному проекту и коллегам (чтобы им тоже было просто читать, править, переиспользовать).
А это можно сделать только путем копания в документации, дебаггинге стек-трейсов, изучением ядра внешних библиотек и "вымучиванию" кода проекта. Тогда это становится твоим "капиталом" - именно общий подход к решению любых задач на любом стеке.
ИИ предлагает вероятностный код как со StackOverflow, ты его просматриваешь и не вчитываешься - задачу решает, тесты проходят, стиль немного "не твой", ну и ладно. И сразу же теряешь экспертизу, потому что не сам прошел путь написания, не выстрадал решение и оно не стало твоим "капиталом" - это просто копи-паста, которая не развивает как инженера.
Даже в рутинных задачах лучше заниматься кодом самому - видишь, где можно оптимизировать, где сделать автогенерацию, например readFile -> regexp(endpoints) -> writeFiles(boilerplate service + tests + types). Иногда приходят мысли об изоляции или наоборот универсализации для сокращения кода и удобства управления. В итоге через какое-то время достаточно 1 строчки кода для нового функционала - остальное генерируется стандартно, стабильно, отказоустойчиво, без лишней когнитивной нагрузки и за 0.01с при изменении файлов, которые наблюдаются file watcher.
И получается что "30% экономии времени с ИИ" превращается в "через год все заросло дубляжом и легаси, потому что не оптимизировалось и упрощалось вживую" + "ухудшились навыки как инженера на 30%, потому что разучился тщательно продумывать решения". В итоге - деградация и скатывание в написание промптов и оркестрацию.
На начальном этапе это совсем не заметно - сейчас экспертиза, навыки, понимание есть, как и работает архитектурное и инженерное мышление. Поэтому все графики работы с ИИ показывают вначале бурный рост. А потом - сильное замедление по разным причинам, которые много раз описывали, и деградация квалификации - лишь один из факторов.
Идею уже выразил в этом комментарии, а разбавлять ее водой в статье не вижу смысла... Всегда на этапе вхождения в программирование лучше начинать с комплексных фреймворков, где все согласовано между собой и продумано. А когда начинаешь ловить себя на мысли "здесь и здесь можно сделать лучше, но внедрить это во фреймворк можно только костылями" - то открывается мир "сборки по кирпичикам". И оказывается, что в сообществе программистов огромное количество гранулярных библиотек, которые хорошо делают ясно описанный набор задач, остается только удобно их связать. Либо предложить свое видение миру в виде такой гранулярной библиотеки - для этого не нужна огромная команда и финансирование, а достаточно времени по вечерам.
Самое главное - давать удобные "коннекторы" для других "кирпичиков". В случае Mobx даже их не требуется - синтаксис остается идентичным, как если Mobx вообще не подключать, а писать на голом JS. То есть по умолчанию с ним совместимо большинство js-библиотек, если конечно у них не встроена своя система состояний.
Вы как-то не совсем правильно сравниваете Mobx и Reatom.
Mobx - шуруповерт без насадок. Он добавляет в JS реактивность для объектов, то есть если меняется свойство - то вызываются подписчики, все. Это хороший мотор, который может крутить все что угодно. Это не СТМ, а очень крутой полифилл для deprecated нативного Object.observe. Если нужны "насадки" (экосистема) - этим занимаются другие библиотеки. Можно писать обычный код на js с обычными объектами/классами, но только реактовые компоненты не будут ререндериться. Добавляем observable+observer - все точечно ререндерится.
Reatom - это большая коробка отверток "на все случаи жизни". Как вы заметили - туда запихнули работу с асинхронностью, роутер, ленивый фетчинг, кешы и механизмы для работы с апи, снепшоты состояний и даже sleep функцию. Это можно сказать целый фреймворк со своими гайдлайнами и способами написания кода (например, что свойства надо вызывать как функции counter()). То есть нужно изначально писать на Reatom весь код, нельзя написать на js-объектах и обернуть в пару функций, чтобы все стало реактивным.
Странно слышать, что в Mobx "нет этого или того-то", у него абсолютно другая парадигма - "делать свое дело и делать хорошо". А у ряда библиотек, например Реакта, другая парадигма - "сделаем все-в-одном". Вместо того, чтобы просто рендерить компоненты в DOM и делать это хорошо, они стали внедрять свой стейт-менеджмент, асинхронные механизмы, серверные компоненты, навязывать парадигмы вроде иммутабельности и правил хуков. Да и у ряда компаний есть такой подход - Яндекс например или Сбер стараются охватить все рынки, от образования и развлечений до доставки еды, платежных систем и такси.
Кому-то нравится, когда все в единой экосистеме и "поддерживается одной командой", пусть определенные части сделаны откровенно ужасно (мы ведь тут в статье про Реакт обсуждаем СТМ для него, а ведь у него уже есть встроенная СТМ, просто она слишком плоха). А кто-то выбирает пользоваться специализированными сервисами/библиотеками с ясной ответственностью, которые сделаны максимально качественно. И чем больше насмотренности, экспертизы и опыта, тем менее хочется мириться с подходом "все-в-одном", и вы тут с нами в одной лодке, отказавшись от реактовых подходов. Но в Реатоме я как и другие вижу много недостатков, и не готов полностью на него переходить, а выберу лучше несколько специализированных библиотек.
Я тоже работал в проектах, где на Mobx создавали абсолютно неподдерживаемого монстра, используя все возможные антипаттерны. Например, использовали автонормализацию snake_case -> camelCase при получении данных из сторонних источников, что ломало быстрые переходы и поиск по проекту. Делали огромные Rich Store на тысячи строк, неконтролируемые autorun во всех возможных местах, использовали одновременно реактивные состояния и useState/useEffect, причем пытаясь из их комбинации сделать computed. По структуре тоже была каша - и Entity Stores, и сервисы со своими сторами, и модульные, и глобальные в несколько уровней.
Так что ваш аргумент тоже корректен - чем более гибок инструмент, тем легче писать некачественный код. Но вот еще один поинт - из Mobx очень легко сделать Redux. Просто добавить в ваш пример в статье метод dispatch(action, payload) , который будет делать Object.assign(this._state, payload) в мутабельном стиле (для оптимизации изменения глубоко вложенных структур придется чуть усложнить). Ну и switch-case туда. При этом рендеринг UI компонентов будет оптимизирован из коробки - что бы мы ни меняли, будут точечно ререндериться только те компоненты, которые читают конкретный параметр, и только когда он изменился.
А вот из Redux и других мутабельных стейт-менеджеров сделать реактивную систему не получится, так как ссылки на структуры данных постоянно меняются, и постоянно нужно делать селекторы вручную и писать системы сравнения значений.
То есть никто не запрещает написать на Mobx флоу работы с данными как в Redux, описать это в тех. документации и на ревью проверять, что все разработчики придерживаются этого флоу. Либо не Redux-like, а выбрать удобный для проекта и команды способ установки-получения значений, ведь этих паттернов огромное количество, и Mobx с очень большой вероятностью сможет так работать. То есть он - это все остальные СТМ вместе взятые, остается только выбрать удобный подход и консистентно работать с ним.
В Mobx тоже перерисовка связана явно, только вместо dispatch с полным пересозданием структуры здесь просто мутация store.param = value. Это дает намного более удобный DX - можно в IDE посмотреть все места, где читается/изменяется конкретное свойство и работают быстрые переходы.
А в Redux нужно сначала найти нужный initialState, потом найти десяток редюсеров, которые меняют в нем param, потом найти action name, по нему сделать полный поиск по проекту, чтобы найти все места где делается dispatch с этим названием экшена, не забыть пройтись по селекторам и connect HOC, чтобы узнать, где используются конкретный стор, а потом в каждом найденном компоненте искать где читается param. К сожалению, довольно много работал с Redux и пол-дня состояло как раз из этих действий, потому что никаких быстрых переходов или Find Usages у него не предусмотрено.
И с Redux в итоге не так-то просто найти, "почему компонент перерендерился", потому что нужно смотреть в Dev Tools и вручную искать какой именно из диспатчей данных повлиял на конкретный компонент, проходя все этапы, описанные выше. Но да, в нем есть диффы состояний, которые немного могут упростить процесс.
Дебажить кейс "почему компонент перерендерился" в целом непросто в любом подходе, и помогает тут скорее создание более атомарных компонентов, а не монстров с десятками состояний. Но с Mobx это как правило не нужно - компонент ререндерится при изменении читаемых свойств и если он перерендерился - значит нужно. Это не реактовый или редаксовый стейт, где все ререндерится даже если нечитаемые поля изменились, если тщательно не оптимизировать все через селекторы. Поэтому такой проблемы и не возникает
На Mobx можно писать так же, как в примере на Valtio, используя вместо proxy функцию observable. Единственное отличие в коде будет в том, что Mobx подключается к реактовым компонентам через враппинг в HOC observer, а Valtio - через useSnapshot. Плюсы Mobx подхода с враппингом в том, что можно использовать хоть 100 разных реактивных состояний в компоненте напрямую, без вызова для каждого useSnapshot, а сам враппинг можно сделать автоматически через бандлер, то есть компоненты в коде будут выглядеть чистыми.
Вариант с классовыми сторами просто более удобен в плане DX, поэтому часто выбирают его (меньше импортов и констант, меньше коллизии имен, не важен порядок объявления методов и геттеров). Но можно писать и объектами
Хорошо структурированный код - это не только разбиение по семантическим папкам (пакетам) и отдельным файлам со связанной логикой и заботой о переиспользовании. Это еще и удобный DX в виде быстрых переходов на место объявления. Здесь же чтобы попасть в нужное место приходится делать десяток быстрых переходов, причем иногда они динамические и могут быть не связаны типами, и приходится делать "поиск по всем репозиториям". Другими словами - "обертка на обертке с названиями переменных в 1 символ".
Также подобный подход очень усложняет дебаг. Если вы видели стектрейсы реактовых ошибок со стеком вызовов в 50 функций, то наверняка отказались от идеи по ним выяснить проблему. В правильно структурированных библиотеках же стектрейс может быть очень полезен и поможет быстро найти проблему.
Конечно, 5000 строк лапши - это никуда не годится, но и с реактовым подходом тоже не все гладко, и можно было бы сделать лучше.
Я тоже не сравнивал preact/signals-react с нативным Реактом, а сравнивал с Mobx, и сравнение не в пользу преактовых сигналов. Из Mobx можно сделать такой же синтаксис, как практически в любых других реактивных системах, а вот из них сделать Mobx - очень нетривиально. Причем он уже существует практически 10 лет и полноценных альтернатив сейчас нет - Mobx сообщество регулярно исследует и сравнивает все новинки в мире реактивности, и даже создает облегченные альтернативы.
Вы говорили о преимуществах preact/signals-react в том, что они легче, производительнее и могут в Реакте обновлять DOM-ноды напрямую, что не соответствует действительности. "Как с помощью mobX ... Вопросы риторические, потому что ответ на них - никак" тоже не соответствует действительности, и я показал механизмы, как это сделать, причем в preact/signals-react решение хуже.
Ререндерится только Child, а его родитель App - нет. С preact/signals-react схема та же самая, только вместо useState используется useSyncExternalStore, что позволяет менять данные из-вне Child компонента.
Ни о каком точечном обновлении DOM-нод речи в контексте React + preact/signals-react не идет, просто создание реактовых компонентов скрыто "под капот" - а именно записано в прототип сигналов, утяжеляя их. По той же схеме можно сделать и Mobx и любую другую сигнальную систему реактивности, пример приводил в предыдущем сообщении.
Так как в этой связке "изменить значение ноды в браузере(div'a, span'a), не являющейся отдельным компонентом"? Думаю, я прочитал уже большую часть исходников, и такого механизма в этой связке нет, всегда будут создаваться довольно неоптимизированные реакт-компоненты для "якобы точечной реактивности". Если не прав - расскажите, как этого добиться.
Думаю, это удовлетворяет всем требованиям - никаких ререндеров, изменение значения конкретной DOM-ноды. Разумеется, можно вынести механизм в кастомный хук и использовать в виде пары строк или встроить в VM.
К сожалению, Реакт из-за VDOM очень ограничен в альтернативах и обойти его механизмы рендеринга другими путями непросто. Solid.js в этом плане намного более гибкий и близок к нативному JS, поэтому при использовании Solid.js+Mobx (хотя у него есть довольно неплохой аналог Mobx в виде createMutable) можно обойтись без прямого обращения к DOM-ноде и просто писать в jsx <span>{data.counter}</span>. То есть это ограничение Реакта, а не реактивных систем, какую бы вы ни использовали (Mobx тоже на сигналах сделан, просто называются Atom, потому что когда его делали выбрали немного другую терминологию, но суть та же).
Действительно, при изменении значения сигнала не рендерятся ни App, ни Child1. Потому что если сигнал использовать без value, то на него создается отдельный реактовый компонент (выжимка из кода библиотеки с сокращениями)
Видим, что сигнал через прототип пытается показаться Реакту обычным компонентом и дальше использует стандартные реактовые механизмы для самообновления. Так, при изменении value в store увеличивается счетчик
затем реакт с помощью useSyncExternalStore узнает об изменении и ререндерит этот компонент с сигналом.
Если желания копать код нет - можете посмотреть React Dev Tools и убедиться, что на каждый {signal} в jsx был создан отдельный компонент с пропом data и описанными выше хуками.
При анализе я заметил довольно неэффективный механизм ререндера этого компонента по перфомансу, и вполне вероятно, что аналогичный явный код на Mobx будет производительнее
Также очевидны и недостатки неявного создания компонента через {signal} - это не будет работать в атрибутах className={signal} , так как получается, что в атрибут передается целый реактовый компонент. И есть немало других неудобств при написании кода, например {childOneSignal > 0 && childOneSignal} , разумеется, работать не будет.
почему пользователи mobX не хотят узнавать что-то новое, легковесное и более производительное
Очень хотим, но в данном случае даже в таких базовых сценариях все довольно неоптимистично, не говоря уже о преимуществах Mobx целиком как реактивной системы.
Обычная очередная внутрикорпоративная библиотека, которая интегрируется в корпоративный стек этой компании, частично выпущенная в опенсорс. Вспоминается тот же БЭМ, который подходил если для него уже есть большая унифицированная инфраструктура, а как частичный выпуск в опенсорс был хуже тех же CSS Modules, так как решал проблему изоляции стилей многословно и ограниченно (все равно возможны глобальные пересечения).
По мере развития css в котором теперь и переменные, и функции, и обсуждается внедрение mixins эти подходы с css-in-js смотрятся настолько же устаревшими и неактуальными, но если в конкретной компании под StyleX все подготовлено и унифицировано - то им проще его использовать. Это вовсе не для "долгосрочной поддерживаемости" сторонних проектов, которые будут использовать эту библиотеку. Думаю цель выпуска в опенсорс - использование ресурсов бесплатных разработчиков-контрибьюторов и бесплатного тестирования, как это часто и бывает.
Тут не наш профиль) Статью про блоги / контентные сайты тегнули Реактом зачем-то. И так понятно, что для этих задач даже Wordpress тяжеловат и лучше что-то совсем простое на статике делать с минимальным шаблонизатором и нативным js для кнопки "лайкнуть". И уж точно тут не нужен Next или другой мета-фреймворк.
Люди ругаются, что для простейших задач кто-то тянет инструменты уровня "из пушки по воробьям", и это оправданно. А для веб-приложений действительно нужен полноценный стек. Одно смущает - что кто-то этого действительно не понимает и ставит минусы, думаю тут просто недопонимание.
Ну получилась самая базовая реализация, до production-ready еще пара десятков итераций и переосмысления нужна.
Много бойлерплейта, создание через классы new FormGroup(), new FormContol() - это не сериализуется в json (значит не будет удобного SSR), синтаксис - плоские аргументы ('', [required()]) с "магическим маппингом что первый аргумент - значение, второй - массив валидаторов". Это не расширяется, а валидаторы в массиве - очень неудобно в реальности, лучше объектом.
Вывод в UI недекларативный. Да и в целом может понадобиться много параметров для инпутов - isFocused, isDisabled, options (для селектов), formatters, кастомное поведение валидации на onBlur либо на onSubmit, показ лоадеров и предзагрузка данных, и т.п. То есть лучше всегда оставлять конфиг формы гибким и расширяемым для пользователей библиотеки.
В общем, ничего пока что интересного по сравнению с подходом отсюда .
Согласен, в статье хорошо разобрана одна из сфер, где пригодится BFF. В целом "классическое" его назначение - это SSR / SSG, если проекты клиентские (не внутренние админки в закрытых сетях), контроль CSP (т.к. фронтендерам часто удобнее поддерживать именно там динамические политики, чем лезть в nginx или другой прокси) и изоморфный контроль прав. Вот последнее - это интересная тема, разверну.
Возьмем как раз админку в закрытой сети. Бэк отвечает в целом за авторизацию, плюс есть роли / фича-тогглы и например 100 ручек. Нужна мощная система контроля доступа (например, показ определенного раздела только если есть фича-тоггл, а также гранулярный контроль возможности редактировать/обновлять/удалять/смотреть подразделы). Фронт в любом случае будет это делать if-else для показа элементов, то есть будет обладать 100% информации о том, что где кому доступно. А бэк обладает ограниченной информацией, т.к. часто нужно строить граф зависимостей фича-тогглов (например, раздел "продукты" тянет 10 ручек словарей, ручку авто-анализа, вывода таблицы, редактирования-удаления, стейт-машин и еще несколько смежных). Фронт уже имеет этот граф, и может безошибочно сказать "вот эти 10 ручек должны быть доступны, если включен фича-тоггл просмотр продуктов".
Поэтому в BFF просто можно взять готовый граф из фронта, и отдавать 403 сразу из node.js вместо проксирования в бэк. А поддерживать это все и синхронизировать в реальном бэке - "черная дыра" по ресурсам и потенциальным багам + замедление релизов и усложнение тестирования. Тут BFF для гранулярного контроля выступает отличным ускорителем разработки и достаточной защитой от несанкционированного доступа, тем более что при продуманной архитектуре нужно ровно 0 изменений в BFF - при изменении логики все подтянется автоматически.
Есть и множество более частных применений (например вебсокет-прослойка или SSE), и описанная в статье "подготовка и оптимизация данных под конкретного потребителя" - тоже очень валидный кейс. Особенно BFF помогает, когда узкое место - это количество/качество бэк-команды и она не успевает за фронтом, тогда можно временно "набросать для показа" на node (либо полноценно с интеграцией базы, либо на моках). Бизнес будет доволен, инвестиции будут получены, полноценный бэк - подтянется в своем ритме.
Будет интересно посмотреть, как у вас будут реализованы формы и сравнить. Я обычно беру такой подход (универсальный для React, Solid, Vue, просто движок реактивности разный под капотом - синтаксис полностью одинаковый).
form здесь - независимый реактивный объект, в терминологии Mobx - observable. Расширяемый, полностью типизированный, можно создавать копии или использовать 1 инстанс если нужна синхронизация, SSR из коробки (в том числе с уже выведенными в интерфейс ошибками).
Разметка - декларативная, сами компоненты инпутов просто маппятся по type из любой UI-библиотеки с простой интеграцией (по сути в onChange инпуты меняют соответствующий value).
Соответственно, работают любые реактивные паттерны - computed геттеры (если нужно собрать данные из нескольких полей и по-особому вывести), autorun / reaction если нужна логика взаимосвязанности (например - при изменении одного поля изменить значение в другом или отфильтровать options / перевести в disabled, а также для взаимодействия нескольких форм).
В целом, подходит для сложнейших форм и не натыкался на ограничения. Единственное, что не всем понравится - data-driven (отсутствие каких-либо onChange колбэков). Если надо отреагировать на установку фокуса в инпуте - будет
reaction(() => insuranceStartDate.isFocused, callback), а это часто сложнее воспринимается, чем onFocus, и требует dispose при анмаунте компонента. Но для использующих реактивные подходы - привычно.Будет интересно сравнить с вашим решением.
Вот как раз что я и ожидал - ответ от ИИ, полностью игнорирующий описанный мной концепт, и рекламирующий библиотеку)
i18next, next-intl или vue-i18n - неоптимизированные библиотеки с неэффективным использованием, тут соглашусь, но я же описал подход, никак с ними не связанный. Файлы message.ts - модульные, а общий json нужен только для переводчиков и удобной выгрузки в системы локализации. В коде его вполне можно разбивать на чанки хоть под каждый модуль, хоть под скоуп модулей (например для страницы src/pages/intro брать все тексты из json с этим префиксом). Это гибкость, универсальность и минимальный бойлерплейт.
Конфликты при слиянии веток здесь возможны только если параллельно работают над одним и тем же messages.ts - и точно такие же конфликты будут в Intlayer, так как он тоже использует модульные файлы с локализацией (но вроде без сборки их в большой json). Но это "слабые" конфликты, которые легко решаются автоматически, т.к. все ключи - уникальные.
ИИ захватывает Хабр(
Довольно часто вижу в ленте статьи по Intlayer - вы стараетесь его популяризировать, но не понимаю практической применимости. Обычно переводы в проектах делаются проще и эффективнее.
Посмотрим в контексте "ретроспективного внедрения интернационализации" + React. На данный момент все тексты захардкожены в компонентах, бизнес ставит задачу - перевести на несколько языков.
1.Нужно вынести тексты в объекты, это прекрасно типизируется TS и есть немало инструментов, позволяющих идентифицировать "неиспользуемые ключи в объекте", то есть масштабируемость - хорошая.
2.Вынести объект messages в отдельные файлы для переиспользуемости и задать автогенерацию уникальных ключей
__dirname здесь - простейший уникальный для файла идентификатор, многие бандлеры умеют с ним работать "из коробки". Но можно использовать любой, прокидывая бандлером.
3.Добавить возможность подключить шаблонизатор для плюрализации, работы с датами, падежами и т.п., готовых в js-экосистеме масса. Достаточно сделать функцию ln, а движок подбирать по нуждам проекта либо делегировать это сторонней системе вроде Localise.
Функция ln может под капотом иметь разную реализацию (например, возвращать реакт-компонент) или быть реактивной на сигналах (сменилась локализация - все функции перевызвались, обновив тексты в приложении), это все делается парой строк.
4.Собрать все тексты в json-файлы и встроить генерацию в бандлер перед сборкой и в watch-режиме
Для многих проектов подойдет автопереводчик с ручной правкой, для более сложных - этот json можно автоматизированно выгружать в облачную систему для переводчиков, которые 1 кнопкой пушат его с переводами обратно в репозиторий.
Собственно, все. Это база, которая и так есть в большинстве проектов, и может очень гибко настраиваться под стек, в том числе делая отдельные json-чанки под компонент (чтобы не все локализации грузились сразу). С ИИ этапы 1 и 2 делаются за полчаса, 3 и 4 пишутся за день под конкретные технологии и требования в проекте.
Итог - задача выполнена, при добавлении/удалении ключей в любом модульном messages.ts обновляется JSON, выгружается в систему переводов, и для изменения переводов не нужны разработчики - переводчики могут менять тексты сами, не влияя на код проекта.
А каждый раз, когда захожу на документацию Intlayer, не понимаю зачем столько бойлерплейта и сложностей, зачем вручную задавать ключи с дубляжом для каждого языка; писать переводы прямо в messages вместо того, чтобы отдавать json переводчикам или в автоперевод (поэкономьте время разработчиков); привязываться к встроенным механизмам шаблонизации (когда часто это должно быть частью локализационной системы типа облачного Localise). И много еще другого, что мне кажется крайне неэффективным, и видя это закрываю вкладку с документацией.
Скорее всего, пишу это "в пустоту" - половина ваших комментариев на Хабре ИИ-нейрослоп, и все для рекламы, но слишком уж часто эта поделка попадает в ленту.
К более узкой экспертной специализации так или иначе многие приходят. Если тебя не "ведут за руку" на курсах с трудоустройством, а учишься сам - то в любом случае пробуешь себя в разных сферах.
Раньше вот были профессии "вебмастер" или "веб-технолог" - это и дизайн, и верстка, и фронт, и бэк на CMS, и настройка серверов, и обслуживание / мониторинг, и общение с бизнесом для составления ТЗ, и даже наполнение контентом. Да, глубоко погружаться в каждую область не требовалось - зато позволяло выбрать то, что интересно.
Я тоже выбрал фронт - и это огромная область, к которой сам раньше относился как к "формошлепству", но оказалось что требуется десяток лет, чтобы нормально сделать архитектуру под форму на 50 взаимосвязанных полей с 15 компонентами (range, daterange, tree select) с десятком модалок на заведение новых сущностей тоже с громадными формами, SSR этого всего с асинхронной подгрузкой данных для каждого селекта, параллельной работой по сокету нескольких сотрудников, сохранением недозаполненных форм в Local Storage, фича-тогглами и ролями какой кому показывать инпут а какой отключать в disabled... Это маленькая задача, каких тысячи в реальных проектах среднего (даже не крупного) размера на 2 дня, и нужна мощнейшая автофайлогенерация, продуманные полностью типизированные библиотеки и длинные надежные цепочки отказоустойчивости с мониторингом (иногда - в реальном времени).
"Универсал" такое не делает и поэтому компании ищут именно специализированных фронтендеров, это вполне логично, причем на определенном стеке, потому что под каждый нужен свой набор библиотек и архитектурных подходов. Зато если стек выбран популярный, а не нишевый - то хорошую работу найти намного проще, чем универсалу. Да, пришлось весь нерелевантный опыт "сложить на полку", но все равно широта знаний помогает и можно для интереса брать задачи на фулстак например.
Я думаю, что "наш код" никогда не был чем-то особо секретным. Я фронтендер, весь js код в любом случае должен быть прочитан браузером, а техники "защиты" в виде обфускации не работают, т.к. ключ декодирования все равно должен быть доступен браузеру, а значит - всем кто заходит на сайт. Поэтому по большей части используют просто минификацию, а провести реверс-инжениринг и сделать клон - не такая уж сложная задача и для человека (я много так не-опенсорс библиотек "форкал" и дорабатывал еще лет 15 назад). Для ИИ просто это все намного проще.
По бэку сложнее, но опять же все публичные контракты можно вытянуть и вывести логику через эксперименты в Postman, и запрограммировать на любом удобном языке. Главная ценность в ИТ с давних пор - не код и логика, а данные и привязка аудитории. Так, никому не нужен клон Telegram - его код спокойно лежит в опенсорсе, потому что у клона не будет аудитории. А у клона Хабра - не будет данных и опять же лояльности посетителей.
Так что ИИ не приносит тут ничего нового, если не давать доступ к прод-базе, и уж тем более не сможет эффективно привлекать аудиторию, потому что не имеет мышления людей и понятий о "красоте" или "удобстве".
В статье очень точно описаны и мои наблюдения. Главное в работе инженера-программиста - это постоянно развиваться, продумывать наиболее оптимальные паттерны, подходящие конкретному проекту и коллегам (чтобы им тоже было просто читать, править, переиспользовать).
А это можно сделать только путем копания в документации, дебаггинге стек-трейсов, изучением ядра внешних библиотек и "вымучиванию" кода проекта. Тогда это становится твоим "капиталом" - именно общий подход к решению любых задач на любом стеке.
ИИ предлагает вероятностный код как со StackOverflow, ты его просматриваешь и не вчитываешься - задачу решает, тесты проходят, стиль немного "не твой", ну и ладно. И сразу же теряешь экспертизу, потому что не сам прошел путь написания, не выстрадал решение и оно не стало твоим "капиталом" - это просто копи-паста, которая не развивает как инженера.
Даже в рутинных задачах лучше заниматься кодом самому - видишь, где можно оптимизировать, где сделать автогенерацию, например
readFile -> regexp(endpoints) -> writeFiles(boilerplate service + tests + types). Иногда приходят мысли об изоляции или наоборот универсализации для сокращения кода и удобства управления. В итоге через какое-то время достаточно 1 строчки кода для нового функционала - остальное генерируется стандартно, стабильно, отказоустойчиво, без лишней когнитивной нагрузки и за 0.01с при изменении файлов, которые наблюдаются file watcher.И получается что "30% экономии времени с ИИ" превращается в "через год все заросло дубляжом и легаси, потому что не оптимизировалось и упрощалось вживую" + "ухудшились навыки как инженера на 30%, потому что разучился тщательно продумывать решения". В итоге - деградация и скатывание в написание промптов и оркестрацию.
На начальном этапе это совсем не заметно - сейчас экспертиза, навыки, понимание есть, как и работает архитектурное и инженерное мышление. Поэтому все графики работы с ИИ показывают вначале бурный рост. А потом - сильное замедление по разным причинам, которые много раз описывали, и деградация квалификации - лишь один из факторов.
Идею уже выразил в этом комментарии, а разбавлять ее водой в статье не вижу смысла... Всегда на этапе вхождения в программирование лучше начинать с комплексных фреймворков, где все согласовано между собой и продумано. А когда начинаешь ловить себя на мысли "здесь и здесь можно сделать лучше, но внедрить это во фреймворк можно только костылями" - то открывается мир "сборки по кирпичикам". И оказывается, что в сообществе программистов огромное количество гранулярных библиотек, которые хорошо делают ясно описанный набор задач, остается только удобно их связать. Либо предложить свое видение миру в виде такой гранулярной библиотеки - для этого не нужна огромная команда и финансирование, а достаточно времени по вечерам.
Самое главное - давать удобные "коннекторы" для других "кирпичиков". В случае Mobx даже их не требуется - синтаксис остается идентичным, как если Mobx вообще не подключать, а писать на голом JS. То есть по умолчанию с ним совместимо большинство js-библиотек, если конечно у них не встроена своя система состояний.
Вы как-то не совсем правильно сравниваете Mobx и Reatom.
Mobx - шуруповерт без насадок. Он добавляет в JS реактивность для объектов, то есть если меняется свойство - то вызываются подписчики, все. Это хороший мотор, который может крутить все что угодно. Это не СТМ, а очень крутой полифилл для deprecated нативного
Object.observe. Если нужны "насадки" (экосистема) - этим занимаются другие библиотеки. Можно писать обычный код на js с обычными объектами/классами, но только реактовые компоненты не будут ререндериться. Добавляем observable+observer - все точечно ререндерится.Reatom - это большая коробка отверток "на все случаи жизни". Как вы заметили - туда запихнули работу с асинхронностью, роутер, ленивый фетчинг, кешы и механизмы для работы с апи, снепшоты состояний и даже sleep функцию. Это можно сказать целый фреймворк со своими гайдлайнами и способами написания кода (например, что свойства надо вызывать как функции
counter()). То есть нужно изначально писать на Reatom весь код, нельзя написать на js-объектах и обернуть в пару функций, чтобы все стало реактивным.Странно слышать, что в Mobx "нет этого или того-то", у него абсолютно другая парадигма - "делать свое дело и делать хорошо". А у ряда библиотек, например Реакта, другая парадигма - "сделаем все-в-одном". Вместо того, чтобы просто рендерить компоненты в DOM и делать это хорошо, они стали внедрять свой стейт-менеджмент, асинхронные механизмы, серверные компоненты, навязывать парадигмы вроде иммутабельности и правил хуков. Да и у ряда компаний есть такой подход - Яндекс например или Сбер стараются охватить все рынки, от образования и развлечений до доставки еды, платежных систем и такси.
Кому-то нравится, когда все в единой экосистеме и "поддерживается одной командой", пусть определенные части сделаны откровенно ужасно (мы ведь тут в статье про Реакт обсуждаем СТМ для него, а ведь у него уже есть встроенная СТМ, просто она слишком плоха). А кто-то выбирает пользоваться специализированными сервисами/библиотеками с ясной ответственностью, которые сделаны максимально качественно. И чем больше насмотренности, экспертизы и опыта, тем менее хочется мириться с подходом "все-в-одном", и вы тут с нами в одной лодке, отказавшись от реактовых подходов. Но в Реатоме я как и другие вижу много недостатков, и не готов полностью на него переходить, а выберу лучше несколько специализированных библиотек.
Я тоже работал в проектах, где на Mobx создавали абсолютно неподдерживаемого монстра, используя все возможные антипаттерны. Например, использовали автонормализацию snake_case -> camelCase при получении данных из сторонних источников, что ломало быстрые переходы и поиск по проекту. Делали огромные Rich Store на тысячи строк, неконтролируемые autorun во всех возможных местах, использовали одновременно реактивные состояния и useState/useEffect, причем пытаясь из их комбинации сделать computed. По структуре тоже была каша - и Entity Stores, и сервисы со своими сторами, и модульные, и глобальные в несколько уровней.
Так что ваш аргумент тоже корректен - чем более гибок инструмент, тем легче писать некачественный код. Но вот еще один поинт - из Mobx очень легко сделать Redux. Просто добавить в ваш пример в статье метод
dispatch(action, payload), который будет делатьObject.assign(this._state, payload)в мутабельном стиле (для оптимизации изменения глубоко вложенных структур придется чуть усложнить). Ну и switch-case туда. При этом рендеринг UI компонентов будет оптимизирован из коробки - что бы мы ни меняли, будут точечно ререндериться только те компоненты, которые читают конкретный параметр, и только когда он изменился.А вот из Redux и других мутабельных стейт-менеджеров сделать реактивную систему не получится, так как ссылки на структуры данных постоянно меняются, и постоянно нужно делать селекторы вручную и писать системы сравнения значений.
То есть никто не запрещает написать на Mobx флоу работы с данными как в Redux, описать это в тех. документации и на ревью проверять, что все разработчики придерживаются этого флоу. Либо не Redux-like, а выбрать удобный для проекта и команды способ установки-получения значений, ведь этих паттернов огромное количество, и Mobx с очень большой вероятностью сможет так работать. То есть он - это все остальные СТМ вместе взятые, остается только выбрать удобный подход и консистентно работать с ним.
В Mobx тоже перерисовка связана явно, только вместо dispatch с полным пересозданием структуры здесь просто мутация
store.param = value. Это дает намного более удобный DX - можно в IDE посмотреть все места, где читается/изменяется конкретное свойство и работают быстрые переходы.А в Redux нужно сначала найти нужный initialState, потом найти десяток редюсеров, которые меняют в нем param, потом найти action name, по нему сделать полный поиск по проекту, чтобы найти все места где делается dispatch с этим названием экшена, не забыть пройтись по селекторам и connect HOC, чтобы узнать, где используются конкретный стор, а потом в каждом найденном компоненте искать где читается param. К сожалению, довольно много работал с Redux и пол-дня состояло как раз из этих действий, потому что никаких быстрых переходов или Find Usages у него не предусмотрено.
И с Redux в итоге не так-то просто найти, "почему компонент перерендерился", потому что нужно смотреть в Dev Tools и вручную искать какой именно из диспатчей данных повлиял на конкретный компонент, проходя все этапы, описанные выше. Но да, в нем есть диффы состояний, которые немного могут упростить процесс.
Дебажить кейс "почему компонент перерендерился" в целом непросто в любом подходе, и помогает тут скорее создание более атомарных компонентов, а не монстров с десятками состояний. Но с Mobx это как правило не нужно - компонент ререндерится при изменении читаемых свойств и если он перерендерился - значит нужно. Это не реактовый или редаксовый стейт, где все ререндерится даже если нечитаемые поля изменились, если тщательно не оптимизировать все через селекторы. Поэтому такой проблемы и не возникает
На Mobx можно писать так же, как в примере на Valtio, используя вместо proxy функцию observable. Единственное отличие в коде будет в том, что Mobx подключается к реактовым компонентам через враппинг в HOC observer, а Valtio - через useSnapshot. Плюсы Mobx подхода с враппингом в том, что можно использовать хоть 100 разных реактивных состояний в компоненте напрямую, без вызова для каждого useSnapshot, а сам враппинг можно сделать автоматически через бандлер, то есть компоненты в коде будут выглядеть чистыми.
Вариант с классовыми сторами просто более удобен в плане DX, поэтому часто выбирают его (меньше импортов и констант, меньше коллизии имен, не важен порядок объявления методов и геттеров). Но можно писать и объектами
Хорошо структурированный код - это не только разбиение по семантическим папкам (пакетам) и отдельным файлам со связанной логикой и заботой о переиспользовании. Это еще и удобный DX в виде быстрых переходов на место объявления. Здесь же чтобы попасть в нужное место приходится делать десяток быстрых переходов, причем иногда они динамические и могут быть не связаны типами, и приходится делать "поиск по всем репозиториям". Другими словами - "обертка на обертке с названиями переменных в 1 символ".
Также подобный подход очень усложняет дебаг. Если вы видели стектрейсы реактовых ошибок со стеком вызовов в 50 функций, то наверняка отказались от идеи по ним выяснить проблему. В правильно структурированных библиотеках же стектрейс может быть очень полезен и поможет быстро найти проблему.
Конечно, 5000 строк лапши - это никуда не годится, но и с реактовым подходом тоже не все гладко, и можно было бы сделать лучше.
Я тоже не сравнивал preact/signals-react с нативным Реактом, а сравнивал с Mobx, и сравнение не в пользу преактовых сигналов. Из Mobx можно сделать такой же синтаксис, как практически в любых других реактивных системах, а вот из них сделать Mobx - очень нетривиально. Причем он уже существует практически 10 лет и полноценных альтернатив сейчас нет - Mobx сообщество регулярно исследует и сравнивает все новинки в мире реактивности, и даже создает облегченные альтернативы.
Вы говорили о преимуществах preact/signals-react в том, что они легче, производительнее и могут в Реакте обновлять DOM-ноды напрямую, что не соответствует действительности. "Как с помощью mobX ... Вопросы риторические, потому что ответ на них - никак" тоже не соответствует действительности, и я показал механизмы, как это сделать, причем в preact/signals-react решение хуже.
Расскажите пожалуйста, что я упускаю, и в контексте React + preact/signals-react:
На каждый
{signal}без чтения{signal.value}создается отдельный реактовый компонент, который механизмами реакта подписан на изменениеsignal.valueПри изменении
signal.valueэтот компонент ререндерится, не вызывая рендера родителейТот же принцип на нативном Реакте:
Ререндерится только Child, а его родитель App - нет. С preact/signals-react схема та же самая, только вместо useState используется useSyncExternalStore, что позволяет менять данные из-вне Child компонента.
Ни о каком точечном обновлении DOM-нод речи в контексте React + preact/signals-react не идет, просто создание реактовых компонентов скрыто "под капот" - а именно записано в прототип сигналов, утяжеляя их. По той же схеме можно сделать и Mobx и любую другую сигнальную систему реактивности, пример приводил в предыдущем сообщении.
Так как в этой связке "изменить значение ноды в браузере(div'a, span'a), не являющейся отдельным компонентом"? Думаю, я прочитал уже большую часть исходников, и такого механизма в этой связке нет, всегда будут создаваться довольно неоптимизированные реакт-компоненты для "якобы точечной реактивности". Если не прав - расскажите, как этого добиться.
Для этого есть стандартные механизмы JS DOM API + React useRef. Например:
Думаю, это удовлетворяет всем требованиям - никаких ререндеров, изменение значения конкретной DOM-ноды. Разумеется, можно вынести механизм в кастомный хук и использовать в виде пары строк или встроить в VM.
К сожалению, Реакт из-за VDOM очень ограничен в альтернативах и обойти его механизмы рендеринга другими путями непросто. Solid.js в этом плане намного более гибкий и близок к нативному JS, поэтому при использовании Solid.js+Mobx (хотя у него есть довольно неплохой аналог Mobx в виде createMutable) можно обойтись без прямого обращения к DOM-ноде и просто писать в jsx
<span>{data.counter}</span>. То есть это ограничение Реакта, а не реактивных систем, какую бы вы ни использовали (Mobx тоже на сигналах сделан, просто называются Atom, потому что когда его делали выбрали немного другую терминологию, но суть та же).Я вот вник в
preact/signals-reactпросто для интереса, и никакого "невозможного" или чудес там нет. Возьмем простой примерДействительно, при изменении значения сигнала не рендерятся ни App, ни Child1. Потому что если сигнал использовать без value, то на него создается отдельный реактовый компонент (выжимка из кода библиотеки с сокращениями)
Видим, что сигнал через прототип пытается показаться Реакту обычным компонентом и дальше использует стандартные реактовые механизмы для самообновления. Так, при изменении value в store увеличивается счетчик
затем реакт с помощью useSyncExternalStore узнает об изменении и ререндерит этот компонент с сигналом.
Если желания копать код нет - можете посмотреть React Dev Tools и убедиться, что на каждый
{signal} в jsx был создан отдельный компонент с пропом data и описанными выше хуками.При анализе я заметил довольно неэффективный механизм ререндера этого компонента по перфомансу, и вполне вероятно, что аналогичный явный код на Mobx будет производительнее
Также очевидны и недостатки неявного создания компонента через
{signal}- это не будет работать в атрибутахclassName={signal}, так как получается, что в атрибут передается целый реактовый компонент. И есть немало других неудобств при написании кода, например{childOneSignal > 0 && childOneSignal}, разумеется, работать не будет.Очень хотим, но в данном случае даже в таких базовых сценариях все довольно неоптимистично, не говоря уже о преимуществах Mobx целиком как реактивной системы.