Next.js просто собрал общеизвестные практики, но и без него вполне можно добиться необходимого перфоманса и гибкости.
Например - включить SSR (кастомный делается не то, чтобы сложно) и выбрать одну из стратегий оптимизации:
кеширование запросов в апи. Можно по version, который подается в запросе на бэк, можно по max-age, можно по таймауту, можно с ручной инвалидацией. Таким образом, node.js бэк будет в основном только рендерить html без "тяжелых" асинхронных запросов в бэк, этого достаточно для средней нагрузки
кеширование выдаваемого html - можно в node.js сервере в памяти или быстрой базе (Redis и аналоги), можно в прокси-сервере (nginx, Cloudflare). К этому можно прикрутить те же самые инвалидации по таймауту, max-age или вручную. Получится ISR
Комбинация этих стратегий как раз и даст идеальный баланс, причем из-за гибкости (пришла кука - отдается SSR-страница About, не пришла - отдается из кеша прокси-сервера, запрос из Android или IOS - отдается SSR-страница без вызова апи-запросов, которые берутся из кеша) можно выжать максимум и эффективно защититься от DDOS.
Хорошо, конечно, что в Next.js уже включены несколько вариантов оптимизации, но для требовательных проектов думаю лучше делать кастомные решения.
Я бы тоже хотел подчеркнуть, что "подход с MobX" - это не "подход с MobX", от него только makeAutoObservable как замена forceRender компонентов при изменении данных. 99.99% - это ваш код в ООП стиле и ваша архитектура, на которую Mobx не влияет. На нем можно писать очень по-разному - и без классов, и даже без plain объектов (как я показал на примере функций). Поэтому использование слова Mobx в статье - это только кликбейт, потому что никакой специфики, кроме как автоматических ререндеров UI, он не приносит в контексте статьи, а статья при этом и не про UI, а про совсем "далекую степь" - работу с API.
Отчего у читателя складывается неверное впечатление, будто Mobx хуже React Query в каких-то кейсах, но на самом деле совсем не так - это непересекающиеся вещи, которые нельзя сравнивать, и у которых совершенно разное назначение. То, что можно хранить данные в отдельном объекте и обернуть его в observable и то, что можно хранить данные внутри React Query и возвращать их через хук - это хоть и звучит похоже (объект там и там), но вещи совсем разные. Да, если пытаться использовать стейт вне рендера (mobx ни при чем, просто класс или объект) и стейт возвращаемый React Query внутри функции рендера, то будет дубляж данных, в этом плане их действительно можно противопоставить. Но противопоставление именно отдельного стейта и внутрикомпонентного стейта, с реактивностью это никак не связано.
Хотя React Query тоже кеш хранит глобально, и получается тройной конфликт (глобальный стор скрытый внутри React Query, его выдача в хуке внутри рендера, попытка вынести в независимый слой вне Реакта). Тут действительно можно сказать - либо React Query либо независимый слой хранилищ, потому что все вместе - большая каша. Но опять же, это не про реактивность)
Спасибо за статью, но сравнивать Mobx, который просто добавляет реактивность объектам (можно его воспринимать как полифилл для deprecated метода Object.observe), и библиотеку для запросов не слишком корректно. На мой взгляд, в статье нужно было сравнивать "подход на классах, в котором самостоятельно строится работа с API" и "готовая библиотека, завязанная на Реакт и работающая на хуках". Зачем здесь упомянут Mobx, который просто вынесенные в классы данные делает реактивными, из статьи неясно. Можно просто убрать makeAutoObservable и сделать ручной forceUpdate компонентов либо увеличивать counter, и весь код и проблемы останутся такими же, только без упоминания Mobx в статье.
Все описанные недостатки относятся к архитектуре и реализации в вашем проекте (повторные запросы, громоздкие сторы с длинной цепочкой наследования, ручная обработка состояний загрузки, отсутствие строгих правил реализации) и говорят только о том, что команде такой подход не подошел - не получилось грамотно выделить хранилища и логику и отвязать от UI. А вот подход с хуками и привязкой к рендеру команде более подошел. Соответственно, вывод должен был быть "в нашей команде не получилось построить эффективную архитектуру работы с API, поэтому взяли готовую библиотеку".
Но это вовсе не значит, что у других команд тоже не получится без сторонних библиотек покрыть все эти кейсы, и в ряде случаев это будет эффективнее и удобнее, чем React Query. Например, последние 6-7 лет я использую подход, который базово описал здесь.
ручки описываются 1 раз
валидаторы запроса-ответа генерируются автоматически из TS-моделей
уникальные идентификаторы уже есть (название файла, а при динамических параметрах можно считать hash от request body)
каждая функция как объект обладает реактивным состоянием getUser.state.isExecuting, getUser.state.error
несложно сделать кеш, сравнивая getUser.state.validTill - Date.now(), то есть функция будет просто не вызывать запрос на бэк, а данные будут браться из store.user пока не истечет это время. И много других интересных преимуществ, подробнее описал там
Но кеширование на фронтенде всегда было плохой идеей, так как только бэк знает, когда обновились данные. И это должно настраиваться либо http-заголовками (чтобы браузер сразу брал из кеша), либо передачей version в запросе-ответе, если цель - оптимизировать запросы в БД (бэк, увидев совпадение version, отдаст данные из кеша, чем кардинально оптимизирует нагрузку). Либо если очень нужен фронтовый кеш - то его можно неинвазивно прикрутить практически к любому проекту через Service Worker self.addEventListener("fetch") и не менять ни одной строчки кода проекта, а бонусом - еще и оффлайн-режим и удобный неинвазивный механизм моков.
И только в самом крайнем случае стоит вшивать кеши в код в императивном порядке (хотя примера придумать не могу). К слову, если данные класть в глобальный стор, а не в постраничный (который разрушается при переходе на другую страницу), то в классовом подходе кеш уже получится автоматически
так в фоне запрос вызовется, но компонент будет отрисован сразу из "кеша" - данных в сторе, поэтому ни скролл не сбросится, ни визуальная скорость приложения не пострадает. А сам запрос можно оптимизировать рецептами, которые выше привел.
Еще момент про SSR - его тоже несложно сделать без готовых библиотек. Как известно, useEffect на сервере не вызывается, но вот useState(() => api.getUser()) вызовется. И если как выше писал api выделить в отдельный слой, то можно сделать
// вызов всех апи из любой вложенности компонентов
renderToString(App);
// псевдокод, реальный можно в репо посмотреть
await api.every(fn => !fn.state.executing);
api.every(mock);
res.send(renderToString(App).replace('SERVER_DATA', JSON.stringify(globalStore)));
Вот и весь SSR, поддерживающий любую вложенность - то есть не как в Next, где запросы можно вызывать только на страницах, а можно в 20 глубине вложенности (например в каком-то селекте) вызвать апи, и данные сохранятся в стор и сериализуются для клиента. При этом независимый слой апи полностью отвязан от UI и можно без проблем использовать в любой рендерилке. Если нужен CSR-only, можно по урлу отдавать с бэка пустой html (то есть в роутинге сделать 1 настройку - ssr: true | false для конкретного роута).
В общем, если не хочется использовать готовые библиотеки типа React Query, то вполне можно придумать множество интересных и эффективных подходов, при этом MobX как полифилл реактивности очень помогает "дружить" независимые слои (сторы, апи, UI разных фреймворков). А чтобы в проекте были "строгие правила реализации" нужно не ставить 100500 сторонних библиотек с тоннами документации (это структурирует только микрочасть проекта), а должен быть грамотный техлид или архитектор, который будет синхронизировать команду.
Изначально наткнулся на статью из-за слова MobX, но про него тут ничего нет, поэтому в целом описал альтернативные подходы для решения проблем, описанных в статье)
Для новых библиотек стратегия "esm-only до первой жалобы" кажется адекватной
Думаю, что для них как раз не подойдет. Первые пользователи будут скачивать, видеть что в их проекте не заводится, может быть сотый или тысячный заведет ишью, которое еще будет висеть, пока у мейнтейнера руки дойдут (а может он посчитает, что ему cjs не нужен - значит никому не нужен). Несложно сразу сделать сборку в 2 формата и увеличить охват потенциальных пользователей, вполне возможно, что среди них найдутся активные опенсорсеры, которые помогут развить библиотеку.
А на dual packaging жаловаться 100% никто не будет - мало кто лезет в node_modules чтобы смотреть финальные js-файлы, и еще меньше тех, кто заботится об экономии десятка килобайт в этой папке (особенно в современных проектах, где бандлер, линтер и фреймворк заставляют скачивать десятки доп. пакетов, как правило безальтернативных).
Минус dual packaging как верно замечено - только в увеличении node_modules и соответственно размера скачиваемых файлов. Для небольших библиотек это не то, чтобы проблема. Но вот если пакет резко переходит на esm-only (как например Chalk 5 в 2021 году), то только ради него переводить большие проекты на esm - не стоит усилий и бизнес не даст денег на рефакторинг без практической пользы. Тем более что тогда у node.js были серьезные проблемы с использованием cjs из mjs и многие пакеты не заводились, также и старые версии TS влияют (а их апгрейд связан с большими сложностями).
Даже сейчас есть немало проектов на старых node.js и TS, и если работа проектная или фрилансерская, но требуется использовать какой-нибудь новый пакет - то будет однозначно выбрана версия с dual packaging. И я очень сомневаюсь, что это 10% кейсов, скорее думаю больше 50%, по крайней мере из того, что мне встречается. Вывод - свои библиотеки выдаю в обоих форматах, а минус в виде небольшого увеличения node_modules несущественен по сравнению с увеличенной вдвое совместимостью.
По крайней мере, на сегодняшний момент так, а через пару лет думаю уже можно на esm-only переходить.
Не знаю о таком сообществе, которое вообще не признает проблем. Хотя бы наличие большого количества стейт-менеджеров и возведение определенных в "стандарт" - явное признание useState и Context несостоятельными в этом плане. И это понимание есть даже у новичков, что сам по себе Реакт крайне несовершенен, и нужно использовать эти "тысячу библиотек", чтобы исправить ситуацию.
На собеседованиях часто слышал "расскажите о недостатках фреймворка/библиотеки/подхода", и если ничего не говорить - то значит опыта было мало и проекты были слишком простые, чтобы на что-то наткнуться. В большинстве случаев этим вопросом можно явно определить джун-миддл. Так с любой технологией, в том числе с Реактом - чем больше его используешь, тем больше видишь недостатков, и тем больше копится база собственных решений, как их обходить.
Я согласен, что mvvm отлично ложится на Реакт, если его использовать для View - и можно например с Mobx, эта связка еще раньше Vue показала, насколько удобно работать с реактивностью. Но в этой связке реактивность "покомпонентная", а не "поэлементная", то есть ререндерятся хоть и целевые компоненты, но целиком - так Реакт работает.
жалуются что в него не пичкают ненужным ему хламом.
А вот здесь наоборот - пичкают и очень активно ненужным хламом, от тьмы лишних хуков до серверных компонентов. Лучше бы сконцентрировались на View, уменьшении размера, увеличении перфоманса и возможности полноценно интегрировать сторонние системы реактивности.
Официально, насколько помню, причина в mixins - не до конца доведенной до ума фиче, в которой можно было переиспользовать логику, содержащую жизненный цикл. То есть это был аналог useEffect. А то, что "многие не умеют работать с this и форумы забиты вопросами - почему onClick={this.handleClick} говорит this is not defined" - это второстепенно.
Нужен был именно механизм разбиения сложной логики на более атомарные части и возможность их комбинации и переиспользования. Но реализация в useEffect с ручными зависимостями и частой необходимостью оборачивать функции в useCallback для сохранения ссылок при очистке `window.removeEventListener` вкупе с "правилами хуков", что нельзя что-то включать-отключать-переставлять, привела к куда большим проблемам.
Solid вполне можно использовать, для простоты можно взять универсальную архитектуру, пример которой приводил здесь. Пока от него только приятные впечатления, если не нужна большая экосистема готовых компонентов. Но не стоит забывать, что на js до фреймворков было написано огромное количество библиотек (да и для Web Components сейчас), и если стереть с них пыль и обернуть в простой компонент - то Solid даже для средних по размеру проектов подойдет.
и менять класс у рутового элемента. А если будет потребность в дополнительном функционале - я привел пример, как его можно просто реализовать. Спор о том, что вам не пригождался дополнительный функционал, и всегда хватает базовой реализации? Ну и отлично. Если будет нужен - будете знать, где найти
А зачем копировать то же самое, что я и написал, и говорить что у меня "велосипед"?) Потому что у меня эти переменные есть еще и в ts и могу их типобезопасно использовать для передачи в сторонние библиотеки для стилизации? Если у вас таких кейсов не было - не значит, что ни у кого нет, очень даже полезно.
Или в том, что в html style их проставляю вместо того, чтобы назначать класс dark или light этому же html элементу? Логично же - для SSR. На странице могут быть определенные прелоадеры или скелетоны до того, как загрузится основной CSS файл. И если переменные уже проставлены в изначальный html, то их сразу можно использовать, а light-dark классы придут только после загрузки css файла и будет перерисовка цветов. Можно, конечно, ключевые стили инджектить напрямую в html, но это сложнее в реализации.
Да и CSS Modules и SCSS куда старее тех же CSS Variables, которые вы используете. Это не новые модные технологии, а наиболее удобные и проверенные временем, и решающие проблемы pure css до сих пор.
Не буду убеждать не использовать БЭМ и TW - это определяется количеством легаси, уровнем разработчиков и другими факторами в конкретной компании. Но по проблемам CSS Modules поговорил бы.
Создание класса в CSS Modules "без костылей" - это не использовать block element modificator, а делать `type Props { variant: 'big', 'small' } <div class={styles[props.variant]} />`. Если нужна строгая проверка по типам - есть плагины для генерации .d.ts файлов стилей, то есть на этапе компиляции подсветит, что в классе не хватает чего-то для нового варианта.
Глобальные стили нужны в основном для неподдерживающих стилизацию библиотек. Не такой уж частый случай в наше время, и :global в паре мест не увеличит сложность, зато явно видно - это блок для стилизации некоего легаси.
Как делать темы:
// themes.scss
.light { --border: #e8e8e8; }
.dark { --border: #ccc; }
// автогенератор делает themes.ts
export const themes = {
light: { "--border": "#e8e8e8" },
dark: { "--border": "#ccc" }
}
// при смене темы ставим в рут
Object.entries(themes.light).forEach(([key, value]) => {
root.style.setProperty(key, value);
});
// используем в стилях через CSS variables
.button { border-color: var(--border) }
Смена без перезагрузки страницы, доп. стилей, работают быстрые переходы в IDE, копеечные трудозатраты на реализацию. Можно и переписывать CSS variables при необходимости в конкретных элементах.
"БЭМ понятен с первого взгляда, а styles.button что означает?" - значит кнопка. Семантическое английское имя вместо нагруженных блоков-элементов-модификаторов
Для универсализации стилей есть прекрасный подход из SCSS - mixins. Делаешь `@mixin button {}` и применяешь там, где нужно. CSS Modules - хоть и инструмент, фокусирующийся на модульности и изолированности, никак не запрещает переиспользование при необходимости за счет других механизмов. Но для оптимизации и правда нужно использовать доп. инструменты, но эта проблема только для легаси проектов. В современных есть разделение кода на чанки, и загружаются только те части, которые нужны текущей странице - и вкупе с минификацией + brotli размер файла со стилями не будет проблемой фактически никогда.
Причем в БЭМ с его длинными названиями и обилием классов и оберток проблема оптимизации может встать раньше, ведь нельзя в бандлере 1 строчкой сократить названия стилей - они всегда будут длинными и раздувать html и css.
Также не забывайте про возможность быстрых переходов в IDE на CSS Modules и куда более простой рефакторинг неиспользуемых стилей. Также CSS Modules позволяют генерировать имена классов по месту использования, становясь около-уникальным идентификатором, и сразу видно в Sentry например при клике на какой компонент произошла ошибка. Ни TW, ни БЭМ такого не умеют.
В целом - все по делу. Реклама канала для статьи из песочницы - плохо, но без минуса, потому что бьет в больное место. На фронте особенно (бэк более консерватичен) - всевозможные "киллеры" выходят день ото дня, но каждый из них убивает время и проверка на практике. Те же CSS-in-JS или Redux и иммутабельность
React как view устарел) Его подход с ререндером компонентов даже с Mobx вызывает всю цепочку сравнений VDOM. В целом он очень неоптимизирован. React is ecosystem пока остается актуальным.
В вашем контексте "разработка навынос", то есть заказная, сроки - сжатые, команда - минимальная и низкой квалификации, статья и аргументы годные для "песочницы". Но Хабру это пользы не несет, поэтому поставил минус за низкое качество материала. После 20 таких проектов - сами увидите, насколько плох был подход и подобранные библиотеки.
"возьми почитал" мобильный Сафари 17.2. Интересно, на какие страны вы делаете приложения - в Америке валом старых айфонов, в России - тем более, в Азии 17 еще не видели.
В контексте "еще в 2021 css убил scss" ну сами посмотрите
Next.js просто собрал общеизвестные практики, но и без него вполне можно добиться необходимого перфоманса и гибкости.
Например - включить SSR (кастомный делается не то, чтобы сложно) и выбрать одну из стратегий оптимизации:
кеширование запросов в апи. Можно по version, который подается в запросе на бэк, можно по max-age, можно по таймауту, можно с ручной инвалидацией. Таким образом, node.js бэк будет в основном только рендерить html без "тяжелых" асинхронных запросов в бэк, этого достаточно для средней нагрузки
кеширование выдаваемого html - можно в node.js сервере в памяти или быстрой базе (Redis и аналоги), можно в прокси-сервере (nginx, Cloudflare). К этому можно прикрутить те же самые инвалидации по таймауту, max-age или вручную. Получится ISR
Комбинация этих стратегий как раз и даст идеальный баланс, причем из-за гибкости (пришла кука - отдается SSR-страница About, не пришла - отдается из кеша прокси-сервера, запрос из Android или IOS - отдается SSR-страница без вызова апи-запросов, которые берутся из кеша) можно выжать максимум и эффективно защититься от DDOS.
Хорошо, конечно, что в Next.js уже включены несколько вариантов оптимизации, но для требовательных проектов думаю лучше делать кастомные решения.
Я бы тоже хотел подчеркнуть, что "подход с MobX" - это не "подход с MobX", от него только makeAutoObservable как замена forceRender компонентов при изменении данных. 99.99% - это ваш код в ООП стиле и ваша архитектура, на которую Mobx не влияет. На нем можно писать очень по-разному - и без классов, и даже без plain объектов (как я показал на примере функций). Поэтому использование слова Mobx в статье - это только кликбейт, потому что никакой специфики, кроме как автоматических ререндеров UI, он не приносит в контексте статьи, а статья при этом и не про UI, а про совсем "далекую степь" - работу с API.
Отчего у читателя складывается неверное впечатление, будто Mobx хуже React Query в каких-то кейсах, но на самом деле совсем не так - это непересекающиеся вещи, которые нельзя сравнивать, и у которых совершенно разное назначение. То, что можно хранить данные в отдельном объекте и обернуть его в observable и то, что можно хранить данные внутри React Query и возвращать их через хук - это хоть и звучит похоже (объект там и там), но вещи совсем разные. Да, если пытаться использовать стейт вне рендера (mobx ни при чем, просто класс или объект) и стейт возвращаемый React Query внутри функции рендера, то будет дубляж данных, в этом плане их действительно можно противопоставить. Но противопоставление именно отдельного стейта и внутрикомпонентного стейта, с реактивностью это никак не связано.
Хотя React Query тоже кеш хранит глобально, и получается тройной конфликт (глобальный стор скрытый внутри React Query, его выдача в хуке внутри рендера, попытка вынести в независимый слой вне Реакта). Тут действительно можно сказать - либо React Query либо независимый слой хранилищ, потому что все вместе - большая каша. Но опять же, это не про реактивность)
Спасибо за статью, но сравнивать Mobx, который просто добавляет реактивность объектам (можно его воспринимать как полифилл для deprecated метода
Object.observe
), и библиотеку для запросов не слишком корректно. На мой взгляд, в статье нужно было сравнивать "подход на классах, в котором самостоятельно строится работа с API" и "готовая библиотека, завязанная на Реакт и работающая на хуках". Зачем здесь упомянут Mobx, который просто вынесенные в классы данные делает реактивными, из статьи неясно. Можно просто убратьmakeAutoObservable
и сделать ручнойforceUpdate
компонентов либо увеличиватьcounter
, и весь код и проблемы останутся такими же, только без упоминания Mobx в статье.Все описанные недостатки относятся к архитектуре и реализации в вашем проекте (повторные запросы, громоздкие сторы с длинной цепочкой наследования, ручная обработка состояний загрузки, отсутствие строгих правил реализации) и говорят только о том, что команде такой подход не подошел - не получилось грамотно выделить хранилища и логику и отвязать от UI. А вот подход с хуками и привязкой к рендеру команде более подошел. Соответственно, вывод должен был быть "в нашей команде не получилось построить эффективную архитектуру работы с API, поэтому взяли готовую библиотеку".
Но это вовсе не значит, что у других команд тоже не получится без сторонних библиотек покрыть все эти кейсы, и в ряде случаев это будет эффективнее и удобнее, чем React Query. Например, последние 6-7 лет я использую подход, который базово описал здесь.
ручки описываются 1 раз
валидаторы запроса-ответа генерируются автоматически из TS-моделей
уникальные идентификаторы уже есть (название файла, а при динамических параметрах можно считать hash от request body)
каждая функция как объект обладает реактивным состоянием
getUser.state.isExecuting
,getUser.state.error
несложно сделать кеш, сравнивая
getUser.state.validTill - Date.now()
, то есть функция будет просто не вызывать запрос на бэк, а данные будут браться изstore.user
пока не истечет это время. И много других интересных преимуществ, подробнее описал тамНо кеширование на фронтенде всегда было плохой идеей, так как только бэк знает, когда обновились данные. И это должно настраиваться либо http-заголовками (чтобы браузер сразу брал из кеша), либо передачей version в запросе-ответе, если цель - оптимизировать запросы в БД (бэк, увидев совпадение version, отдаст данные из кеша, чем кардинально оптимизирует нагрузку). Либо если очень нужен фронтовый кеш - то его можно неинвазивно прикрутить практически к любому проекту через Service Worker
self.addEventListener("fetch")
и не менять ни одной строчки кода проекта, а бонусом - еще и оффлайн-режим и удобный неинвазивный механизм моков.И только в самом крайнем случае стоит вшивать кеши в код в императивном порядке (хотя примера придумать не могу). К слову, если данные класть в глобальный стор, а не в постраничный (который разрушается при переходе на другую страницу), то в классовом подходе кеш уже получится автоматически
так в фоне запрос вызовется, но компонент будет отрисован сразу из "кеша" - данных в сторе, поэтому ни скролл не сбросится, ни визуальная скорость приложения не пострадает. А сам запрос можно оптимизировать рецептами, которые выше привел.
Еще момент про SSR - его тоже несложно сделать без готовых библиотек. Как известно, useEffect на сервере не вызывается, но вот
useState(() => api.getUser())
вызовется. И если как выше писал api выделить в отдельный слой, то можно сделатьВот и весь SSR, поддерживающий любую вложенность - то есть не как в Next, где запросы можно вызывать только на страницах, а можно в 20 глубине вложенности (например в каком-то селекте) вызвать апи, и данные сохранятся в стор и сериализуются для клиента. При этом независимый слой апи полностью отвязан от UI и можно без проблем использовать в любой рендерилке. Если нужен CSR-only, можно по урлу отдавать с бэка пустой html (то есть в роутинге сделать 1 настройку -
ssr: true | false
для конкретного роута).В общем, если не хочется использовать готовые библиотеки типа React Query, то вполне можно придумать множество интересных и эффективных подходов, при этом MobX как полифилл реактивности очень помогает "дружить" независимые слои (сторы, апи, UI разных фреймворков). А чтобы в проекте были "строгие правила реализации" нужно не ставить 100500 сторонних библиотек с тоннами документации (это структурирует только микрочасть проекта), а должен быть грамотный техлид или архитектор, который будет синхронизировать команду.
Изначально наткнулся на статью из-за слова MobX, но про него тут ничего нет, поэтому в целом описал альтернативные подходы для решения проблем, описанных в статье)
Думаю, что для них как раз не подойдет. Первые пользователи будут скачивать, видеть что в их проекте не заводится, может быть сотый или тысячный заведет ишью, которое еще будет висеть, пока у мейнтейнера руки дойдут (а может он посчитает, что ему cjs не нужен - значит никому не нужен). Несложно сразу сделать сборку в 2 формата и увеличить охват потенциальных пользователей, вполне возможно, что среди них найдутся активные опенсорсеры, которые помогут развить библиотеку.
А на dual packaging жаловаться 100% никто не будет - мало кто лезет в node_modules чтобы смотреть финальные js-файлы, и еще меньше тех, кто заботится об экономии десятка килобайт в этой папке (особенно в современных проектах, где бандлер, линтер и фреймворк заставляют скачивать десятки доп. пакетов, как правило безальтернативных).
Минус dual packaging как верно замечено - только в увеличении node_modules и соответственно размера скачиваемых файлов. Для небольших библиотек это не то, чтобы проблема. Но вот если пакет резко переходит на esm-only (как например Chalk 5 в 2021 году), то только ради него переводить большие проекты на esm - не стоит усилий и бизнес не даст денег на рефакторинг без практической пользы. Тем более что тогда у node.js были серьезные проблемы с использованием cjs из mjs и многие пакеты не заводились, также и старые версии TS влияют (а их апгрейд связан с большими сложностями).
Даже сейчас есть немало проектов на старых node.js и TS, и если работа проектная или фрилансерская, но требуется использовать какой-нибудь новый пакет - то будет однозначно выбрана версия с dual packaging. И я очень сомневаюсь, что это 10% кейсов, скорее думаю больше 50%, по крайней мере из того, что мне встречается. Вывод - свои библиотеки выдаю в обоих форматах, а минус в виде небольшого увеличения node_modules несущественен по сравнению с увеличенной вдвое совместимостью.
По крайней мере, на сегодняшний момент так, а через пару лет думаю уже можно на esm-only переходить.
Не знаю о таком сообществе, которое вообще не признает проблем. Хотя бы наличие большого количества стейт-менеджеров и возведение определенных в "стандарт" - явное признание useState и Context несостоятельными в этом плане. И это понимание есть даже у новичков, что сам по себе Реакт крайне несовершенен, и нужно использовать эти "тысячу библиотек", чтобы исправить ситуацию.
На собеседованиях часто слышал "расскажите о недостатках фреймворка/библиотеки/подхода", и если ничего не говорить - то значит опыта было мало и проекты были слишком простые, чтобы на что-то наткнуться. В большинстве случаев этим вопросом можно явно определить джун-миддл. Так с любой технологией, в том числе с Реактом - чем больше его используешь, тем больше видишь недостатков, и тем больше копится база собственных решений, как их обходить.
Я согласен, что mvvm отлично ложится на Реакт, если его использовать для View - и можно например с Mobx, эта связка еще раньше Vue показала, насколько удобно работать с реактивностью. Но в этой связке реактивность "покомпонентная", а не "поэлементная", то есть ререндерятся хоть и целевые компоненты, но целиком - так Реакт работает.
А вот здесь наоборот - пичкают и очень активно ненужным хламом, от тьмы лишних хуков до серверных компонентов. Лучше бы сконцентрировались на View, уменьшении размера, увеличении перфоманса и возможности полноценно интегрировать сторонние системы реактивности.
Официально, насколько помню, причина в mixins - не до конца доведенной до ума фиче, в которой можно было переиспользовать логику, содержащую жизненный цикл. То есть это был аналог useEffect. А то, что "многие не умеют работать с this и форумы забиты вопросами - почему
onClick={this.handleClick}
говоритthis is not defined
" - это второстепенно.Нужен был именно механизм разбиения сложной логики на более атомарные части и возможность их комбинации и переиспользования. Но реализация в useEffect с ручными зависимостями и частой необходимостью оборачивать функции в useCallback для сохранения ссылок при очистке `window.removeEventListener` вкупе с "правилами хуков", что нельзя что-то включать-отключать-переставлять, привела к куда большим проблемам.
Solid вполне можно использовать, для простоты можно взять универсальную архитектуру, пример которой приводил здесь. Пока от него только приятные впечатления, если не нужна большая экосистема готовых компонентов. Но не стоит забывать, что на js до фреймворков было написано огромное количество библиотек (да и для Web Components сейчас), и если стереть с них пыль и обернуть в простой компонент - то Solid даже для средних по размеру проектов подойдет.
Для простых сайтов, конечно, избыточно, можно как я писал обойтись
и менять класс у рутового элемента. А если будет потребность в дополнительном функционале - я привел пример, как его можно просто реализовать. Спор о том, что вам не пригождался дополнительный функционал, и всегда хватает базовой реализации? Ну и отлично. Если будет нужен - будете знать, где найти
А зачем копировать то же самое, что я и написал, и говорить что у меня "велосипед"?) Потому что у меня эти переменные есть еще и в ts и могу их типобезопасно использовать для передачи в сторонние библиотеки для стилизации? Если у вас таких кейсов не было - не значит, что ни у кого нет, очень даже полезно.
Или в том, что в html style их проставляю вместо того, чтобы назначать класс dark или light этому же html элементу? Логично же - для SSR. На странице могут быть определенные прелоадеры или скелетоны до того, как загрузится основной CSS файл. И если переменные уже проставлены в изначальный html, то их сразу можно использовать, а light-dark классы придут только после загрузки css файла и будет перерисовка цветов. Можно, конечно, ключевые стили инджектить напрямую в html, но это сложнее в реализации.
Да и CSS Modules и SCSS куда старее тех же CSS Variables, которые вы используете. Это не новые модные технологии, а наиболее удобные и проверенные временем, и решающие проблемы pure css до сих пор.
Не буду убеждать не использовать БЭМ и TW - это определяется количеством легаси, уровнем разработчиков и другими факторами в конкретной компании. Но по проблемам CSS Modules поговорил бы.
Создание класса в CSS Modules "без костылей" - это не использовать block element modificator, а делать `type Props { variant: 'big', 'small' } <div class={styles[props.variant]} />`. Если нужна строгая проверка по типам - есть плагины для генерации .d.ts файлов стилей, то есть на этапе компиляции подсветит, что в классе не хватает чего-то для нового варианта.
Глобальные стили нужны в основном для неподдерживающих стилизацию библиотек. Не такой уж частый случай в наше время, и :global в паре мест не увеличит сложность, зато явно видно - это блок для стилизации некоего легаси.
Как делать темы:
Смена без перезагрузки страницы, доп. стилей, работают быстрые переходы в IDE, копеечные трудозатраты на реализацию. Можно и переписывать CSS variables при необходимости в конкретных элементах.
"БЭМ понятен с первого взгляда, а styles.button что означает?" - значит кнопка. Семантическое английское имя вместо нагруженных блоков-элементов-модификаторов
Для универсализации стилей есть прекрасный подход из SCSS - mixins. Делаешь `@mixin button {}` и применяешь там, где нужно. CSS Modules - хоть и инструмент, фокусирующийся на модульности и изолированности, никак не запрещает переиспользование при необходимости за счет других механизмов. Но для оптимизации и правда нужно использовать доп. инструменты, но эта проблема только для легаси проектов. В современных есть разделение кода на чанки, и загружаются только те части, которые нужны текущей странице - и вкупе с минификацией + brotli размер файла со стилями не будет проблемой фактически никогда.
Причем в БЭМ с его длинными названиями и обилием классов и оберток проблема оптимизации может встать раньше, ведь нельзя в бандлере 1 строчкой сократить названия стилей - они всегда будут длинными и раздувать html и css.
Также не забывайте про возможность быстрых переходов в IDE на CSS Modules и куда более простой рефакторинг неиспользуемых стилей. Также CSS Modules позволяют генерировать имена классов по месту использования, становясь около-уникальным идентификатором, и сразу видно в Sentry например при клике на какой компонент произошла ошибка. Ни TW, ни БЭМ такого не умеют.
В целом - все по делу. Реклама канала для статьи из песочницы - плохо, но без минуса, потому что бьет в больное место. На фронте особенно (бэк более консерватичен) - всевозможные "киллеры" выходят день ото дня, но каждый из них убивает время и проверка на практике. Те же CSS-in-JS или Redux и иммутабельность
Без гуся нет порося.
Утка наш профиль.
Никогда не верь гусю
Хаб IT-эмиграция здесь зачем? Миграция из прошлого в бункер и на поверхность к роботам?
Статью не читал, но хуки - вы что, шутите?)
React как view устарел) Его подход с ререндером компонентов даже с Mobx вызывает всю цепочку сравнений VDOM. В целом он очень неоптимизирован. React is ecosystem пока остается актуальным.
В вашем контексте "разработка навынос", то есть заказная, сроки - сжатые, команда - минимальная и низкой квалификации, статья и аргументы годные для "песочницы". Но Хабру это пользы не несет, поэтому поставил минус за низкое качество материала. После 20 таких проектов - сами увидите, насколько плох был подход и подобранные библиотеки.
"возьми почитал" мобильный Сафари 17.2. Интересно, на какие страны вы делаете приложения - в Америке валом старых айфонов, в России - тем более, в Азии 17 еще не видели.
В контексте "еще в 2021 css убил scss" ну сами посмотрите
Троллинг? Напишите валидный нестинг на css, опишите поддерживаемые браузеры. Никому не нужен такой бесполезный коммент