Frontender's notes [ru]
Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами. Личный блог автора - @just_genych По вопросам рекламы или разработки - @g_abashkin
Больше📈 Аналитический обзор Telegram-канала Frontender's notes [ru]
Канал Frontender's notes [ru] (@frontendnoteschannel_ru) языкового сегмента Русский является активным участником. Сейчас сообщество объединяет 32 236 подписчиков, занимая 4 192 место в категории Технологии и приложения и 20 089 место в регионе Россия.
📊 Показатели аудитории и динамика
С момента создания невідомо проект демонстрирует стремительный рост, собрав аудиторию из 32 236 подписчиков.
Согласно последним данным от 03 июля, 2026, канал показывает стабильную активность. За последние 30 дней изменение числа участников составило -332, а за последние 24 часа — -13, при этом общий охват остаётся высоким.
- Статус верификации: Не верифицирован
- Уровень вовлечённости (ER): Средний показатель вовлечённости аудитории составляет 7.14%. В первые 24 часа после публикации контент обычно набирает 4.60% реакций от общего числа подписчиков.
- Охват публикаций: В среднем каждый пост получает 2 303 просмотров. В течение первых суток публикация набирает 1 483 просмотров.
- Реакции и взаимодействия: Аудитория активно поддерживает контент: среднее количество реакций на один пост — 10.
- Тематические интересы: Контент сосредоточен на ключевых темах, таких как браузер, api, css, интерфейс, загрузка.
📝 Описание и контентная политика
Автор описывает ресурс как площадку для выражения субъективного мнения:
“Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами.
Личный блог автора - @just_genych
По вопросам рекламы или разработки - @g_abashkin”
Благодаря высокой частоте обновлений (последние данные получены 04 июля, 2026) канал поддерживает актуальность и высокий уровень охвата публикаций. Аналитика показывает, что аудитория активно взаимодействует с контентом, что делает его важной точкой влияния в категории Технологии и приложения.
Загрузка данных...
| Дата | Привлечение подписчиков | Упоминания | Каналы | |
| 04 июля | +1 | |||
| 03 июля | +4 | |||
| 02 июля | 0 | |||
| 01 июля | 0 |
| 2 | Типизация data-атрибутов и Dataset API — type-safe селекторы без рантайм-багов
Data-атрибуты — мощный инструмент для связывания разметки, JS и CSS. Но без типов легко опечататься в data-modal-id или забыть синхронизировать CSS-селектор с JS. В большом проекте это приводит к багам, которые невозможно поймать до тестирования.
Как это выглядит в production
Опишите контракт через TypeScript template literal types:
type DataAttributes = {
'modal-id': 'header' | 'footer';
'theme': 'light' | 'dark';
'row-index': number;
};
Затем генерируйте type-safe селекторы:
type DataSelector<T extends keyof DataAttributes> =
[data-${T}=&quot;${DataAttributes[T]}&quot;];
const headerSelector: DataSelector<'modal-id'> = '[data-modal-id="header"]'; // OK
Dataset API с маппингом
Создайте функцию, которая приводит к camelCase и проверяет типы:
function getDataset<K extends keyof DataAttributes>(el: HTMLElement, attr: K): DataAttributes[K] {
const key = attr.replace(/[A-Z]/g, m => -${m.toLowerCase()});
return el.dataset[key] as DataAttributes[K];
}
Ошибка: забыть, что data-modal-id в dataset становится modalId. Теперь IDE подсветит невалидные ключи.
CSS-переменные из типов
Соберите CSS custom properties автоматически:
const cssVar = --${headerSelector.replace(/[\[\]=&quot;]/g, '').replace(/\s/g, '-')};
// получите --data-modal-id-header
Практические советы
- Добавьте eslint-правило, запрещающее querySelector('[data-*]') без типов. Это ловится статическим анализом.
- Для больших проектов используйте генерацию схемы из единого источника (например, JSON Schema) и экспорт типов — это защитит от рассинхронизации с дизайном.
Вывод: Типизация data-атрибутов через template literal types превращает runtime-ошибки в compile-time, синхронизирует CSS и JS, и снижает когнитивную нагрузку при разработке. | 1 007 |
| 3 | ИИ поможет написать за тебя код, и в то же время помешает найти тебе работу? Ирония судьбы.
Мы уже доверили нейросетям генерацию компонентов, написание целых функций и даже тестов. Но когда дело доходит до твоего резюме — тот же самый ИИ безжалостно отправляет его в корзину.
До 90% откликов отсеиваются алгоритмами ещё до того, как их увидит человек. В 2026 году ты соревнуешься не только с другими разработчиками, но и с машиной, которая оценивает твоё резюме по десяткам критериев за доли секунды.
Время играть по новым правилам.
📅 8 июля в 19:00 МСК на бесплатном вебинаре «Как пробить AI-фильтры и получать приглашения на интервью» мы расскажем, как обойти эту систему.
На эфире разберем:
🔹 Как AI и ATS отбирают кандидатов прямо сейчас
🔹 Как найти работу в США и Европе
🔹 Что такое «теневой рынок» труда и как использовать его возможности
Спикер — Ангелина Волкова, карьерный эксперт AgileFluent.
Ангелина знает, как обмануть AI-фильтры:
✔️ Сама придумывает стратегии, которые ведут к офферу
✔️ Обладает большой экспертизой по рынку Северной Америки
✔️ За плечами — более 200 кейсов с международными офферами
За время работы AgileFluent уже помогли 800+ специалистам из IT и Digital найти работу в России и за рубежом.
Оставляй заявку на вебинар и бронируй место
👉 [ссылка] 👈
Реклама. ООО «Эджайл», ИНН 7810964334, erid: 2Vtzqvs9fcV | 1 115 |
| 4 | CSS в микрофронтендах: почему Layers и Mixins лучше CSS-in-JS
Микрофронтенды в монорепе — штука удобная, пока не касаешься CSS. Каждый микросервис тащит свои стили, потом они накладываются друг на друга, кто-то где-то вставляет !important, начинается хаос. Знакомо.
Попробовал связку: CSS Modules + CSS Layers + PostCSS Mixins. Дает типизацию, изоляцию и контроль над каскадом. Не идеально, но жить можно.
Проблема 1: порядок подключения ломает стили
Решается CSS Layers. Явно задаешь приоритет через @layer. Например, слой base (общие компоненты) всегда ниже слоя components (конкретный микрофронт). Делаешь файл layers.css:
@layer reset, base, components, utilities;
В каждом микрофронте подключаешь:
@import '@company/styles/layers.css' layer(components);
@layer components {
.button {
background: var(--color-primary);
}
}
Порядок гарантирован, даже если импорты разбросаны. Типичная ошибка — мешать @import в разные слои в одном файле. Используй одну точку входа для всех слоев.
Проблема 2: опечатки в styles.myButton
TypeScript тут не поможет — это же CSS. Выход — typed-css-modules. Генерирует .d.ts для всех .module.css. Добавляешь скрипт:
"type-check": "typed-css-modules 'packages/*/src/**/*.module.css' --outDir ."
Получаешь:
// Button.module.css.d.ts
export const button: string;
IDE подсказывает имена, ошибки ловятся на CI. Practical совет: генерируй типы на pre-commit хуке, чтобы не забывать.
Проблема 3: копипаста flex-center, text-truncate по всем модулям
Тут помогает postcss-mixins. Заводишь общий файл:
/* @company/styles/mixins.css */
@define-mixin flex-center {
display: flex; align-items: center; justify-content: center;
}
В модуле:
@import '@company/styles/mixins.css';
.card-header {
@mixin flex-center;
gap: 0.5rem;
}
Кода меньше на 30-40%. И стили синхронизированы по всему монорепу. Важно: в продакшене убедись, что postcss-import обрабатывается до postcss-mixins, иначе миксины не раскроются.
Архитектура в Nx/Turborepo:
- packages/shared/styles — базовые слои + миксины
- packages/shared/ui-kit — компоненты с typed-css-modules
- apps/mfe-* — каждый микрофронт подключает общие стили, но со своими слоями
Ключевые trade-offs: меньше рантайма, чем CSS-in-JS, безопаснее голого CSS. Надежность растет за счет типизации и изоляции каскада. Поддержка масштабируется на сотни компонентов.
Ссылки по теме:
CSS Layers спецификация
typed-css-modules
postcss-mixins
Вывод: Явное управление каскадом через CSS Layers, типизация через typed-css-modules и переиспользование через PostCSS Mixins превращают CSS Modules из опасного зверя в предсказуемый инструмент для микрофронтендов в монорепе. | 1 181 |
| 5 | Онлайн-магистратура для IT: ИТМО, МИФИ + Яндекс
Программы онлайн-магистратуры ИТМО и МИФИ в партнёрстве с Яндексом. Актуальные знания, практическое обучение и гибкий график. Учитесь, совмещая с работой. Доступна господдержка оплаты, отсрочка от армии
Перейти на сайт
#реклама 16+
practicum.yandex.ru
О рекламодателе | 1 602 |
| 6 | Как мы ускорили разработку Frontend в 10х: TSGO, Oxlint, Rsbuild, React Compiler & CodeGen
В статье разбираются пять направлений, в которых получен измеримый эффект. Первое направление — type checking: сравнение TSCheck и TSGO. Второе — linting: сравнение ESLint, Biome и Oxlint. Третье — bundling: переход от Webpack к Vite, затем к Rsbuild. Четвертое — API-контракты: кодогенерация без AI. Пятое — React-оптимизации: использование React Compiler в production.
Читать на Habr | 1 646 |
| 7 | Yandex Ecom Open Air — летнее событие про онлайн-продажи
Yandex Ecom Open Air 2026 объединяет деловую программу, живое общение и атмосферу фестиваля в одном потоке. Здесь обсуждают технологии, которые становятся частью среды. Исследуют силы, которые влияют на весь рынок. Находят новые связи, идеи и точки роста.
В течение дня пространство фестиваля наполняют выступления, дискуссии, встречи, специальные форматы, музыка и активности партнёров.
Присоединиться к происходящему можно из любой точки — на площадке фестиваля или через онлайн-трансляцию.
Зарегистрироваться
#реклама 18+
ecomfest.ru
О рекламодателе | 1 605 |
| 8 | Конец бесплатного PrimeNG, PrimeReact и PrimeVue? Разбираемся, что задумала PrimeTek
PrimeTek, бывшая PrimeFaces, запустила PrimeUI — новую лицензионную модель для экосистемы PrimeNG, PrimeReact и PrimeVue. Это подаётся как унификация бренда, но по сути компания собирает свои ключевые продукты под одной коммерческой оболочкой. Раньше они существовали как набор отдельных библиотек, которые монетизировались через дополнительные продукты и сервисы. Теперь вся экосистема превращается в единый лицензируемый актив. В статье разберёмся, что именно изменилось, что останется бесплатным, в чем сильные стороны новой схемы и какие риски она несёт для команд и компаний.
Читать далее | 1 676 |
| 9 | Утраиваем бюджет на продвижение в Директе
Запустите первое продвижение в Яндекс Директе
с утроенным бюджетом и ИИ-помощником ✨
Используйте один из промокодов :
При пополнении от 10 000 ₽
+20 000 ₽
Промокод START20
При пополнении от 15 000 ₽
+30 000 ₽
Промокод START30
Зарегистрироваться
#реклама
direct.yandex.ru
О рекламодателе | 1 688 |
| 10 | Рефакторинг SSR: renderToString и renderToPipeableStream в React 19
Долгое время renderToString был монолитом: блокировал event loop до полного выплеска HTML, игнорируя Suspense-границы. Результат — пустая страница для пользователя, пока не загрузится всё. React 19 переосмысливает этот подход, добавляя гранулярное кэширование фрагментов и асинхронную потоковую отдачу.
Как Suspense ломает статус-кво
В React 19 renderToString научился работать с Suspense через механизм компенсированного выброса. Идея: React выдаёт готовый HTML, а то, что висит на Suspense, досылает через renderToPipeableStream. Это означает, что TTFB падает на 40-60%: не нужно ждать все данные сразу, браузер получает каркас страницы, а остальные куски с данными подгружаются постепенно.
Гранулярное кэширование: не трогай статику
Ключевая фича — кэширование отдельных Suspense-границ. Берёшь «шапку» и «блок комментариев» и кэшируешь их по отдельности. Если в комментариях данные устарели, шапка берётся из кэша без перерендера. Пример:
const fragmentCache = new Map();
function renderPage(req, res) {
const stream = renderToPipeableStream(
<Page userId={req.userId} />,
{
onShellReady() {
res.write('');
stream.pipe(res);
},
onFragmentComplete(id, html) {
fragmentCache.set(id, html);
}
}
);
}
Здесь onFragmentComplete даёт доступ к HTML каждого фрагмента по его id. Можно складывать в Map, Redis или CDN. Статичные блоки (шапка, подвал) перестают рендериться заново, что снижает нагрузку на сервер.
Типичная ошибка: игнорировать React Flight
Внутри React 19 renderToPipeableStream использует React Flight: режет HTML на чанки, каждый фрагмент — это свой Promise. Когда данные готовы, отправляется чанк. Старый renderToNodeStream выпилили. Ошибка — продолжать использовать renderToString для страниц с динамическими частями (лента, комментарии, графики). Это приводит к блокировке event loop и росту TTFB, особенно при высоком RPS.
Практический совет: начинай с анализа
Не беги внедрять гранулярное кэширование на каждую страницу. Для простых статичных страниц это оверинжиниринг. Но если у тебя продуктовая страница с частыми обновлениями (например, корзина или рекомендации), профит очевиден. Используй профилировщик React (например, React DevTools Profiler) и измеряй TTFB и TBT до и после. Оптимизируй только узкие места.
Вывод: Гранулярное кэширование фрагментов в React 19 через Suspense и renderToPipeableStream — это production-ready паттерн для снижения TTFB и серверной нагрузки на динамических страницах, но требует вдумчивого выбора кандидатов для оптимизации. | 1 496 |
| 11 | 📣 Появился новый шанс стать ИИ-специалистом и быстро найти работу
МТС и НИУ ВШЭ объявили о старте набора на третий поток магистратуры «Исследования и предпринимательство в искусственном интеллекте». В программу включили 30 оплачиваемых мест от МТС и подготовку в области машинного обучения и ИИ.
Для студентов добавили курсы по генеративному искусственному интеллекту, интеллектуальным агентным системам и проектированию ML-систем. Само обучение проходит на реальных кейса, а лучшие студенты смогут пройти стажировку или получить предложение о работе в МТС Web Services прямо во время обучения.
Подать заявки на обучение можно по ссылке. | 1 602 |
| 12 | Типизация императивных handlebars-шаблонизаторов в JSX через branded типы и кастомные JSX-фабрики
Переход с Handlebars на React часто оставляет за собой строковые шаблоны с {{#each}} и {{#if}}. Прямая передача таких строк в JSX ломает TypeScript, но есть способ это контролировать на уровне типов.
Branded типы как контракт данных
Branded тип - это строка с уникальным символом, которая не может быть создана без специальной фабрики. Обычная строка не скомпилируется в такой тип, что предотвращает случайную передачу сырого шаблона.
type HandlebarsTemplate = string & { __brand: 'Handlebars' };
function hbs(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate {
return strings.reduce((acc, str, i) => acc + str + (values[i] || ''), '') as HandlebarsTemplate;
}
function jsx(tag: string, props: { template?: HandlebarsTemplate } | null, ...children: any[]) {
if (tag === 'Template' && props?.template) {
return compileAndRender(props.template);
}
return React.createElement(tag, props, ...children);
}
Типичная ошибка и её устранение
Если передать сырую строку напрямую, TypeScript выдаст ошибку:
// ❌ Ошибка: Type 'string' is not assignable to type 'HandlebarsTemplate'
<Template template="<div>{{user}}</div>" />
Корректный вариант с фабрикой:
const template = hbs<div>{{#if user}}Hello {{user.name}}{{/if}}</div>;
function MyComponent() {
return <Template template={template} />;
}
Production-oriented практика
Чтобы кастомная JSX-фабрика работала, настрой Babel или TypeScript: добавь @jsx jsx в начале файла или укажи jsxFactory: 'jsx' в tsconfig. Для гибридных проектов это гарантирует, что ни один сырой шаблон не проскочит в рендер без компиляции. Если шаблон использует переменные контекста, добавь generics для типизации payload:
function hbs<T>(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate<T>;
Trade-offs
Дополнительный слой абстракции усложняет читаемость и увеличивает bundle, если шаблонов много. Используй такой подход только в пограничных сценариях миграции, а не как постоянную практику.
Вывод: Branded типы и кастомные JSX-фабрики позволяют безопасно встраивать императивные шаблоны в декларативный JSX, предотвращая runtime-ошибки на этапе компиляции. | 1 563 |
| 13 | Typed custom hooks: constraints и infer для строгих конфигов
Часто вижу, как custom hooks принимают конфиги, где TypeScript выводит mode: string вместо 'edit' | 'view', а permissions — string[] вместо конкретных литералов. В production это ведет к потере автодополнения и багам на стороне потребителей.
Constraints: фиксируем рамки
Используй extends уже на уровне дженериков хука, чтобы сузить допустимые типы. Без него TS выводит широкие типы, с ним — точные литералы, если исходные данные переданы с as const.
function useFeature<M extends string, P extends string[]>(config: {
mode: M;
permissions: P;
}) {
// TS: M, P как литералы
}
Infer: выводим сложные зависимости
Когда конфиг содержит вложенные коллбэки или динамические ключи, infer в условных типах автоматически извлекает нужные типы из переданного объекта. Это спасает от ручного аннотирования.
type ConfigResult<T> = T extends { mode: infer M; permissions: infer P }
? { mode: M; permissions: P }
: never;
Типичная ошибка: забытый as const
Без as const TS выведет string, а не литералы. А если конфиг приходит из API — этот подход бесполезен. Зато для статически заданных конфигов в хуках (фичи, A/B-тесты, permission guards) это чистый профит: типы не протекают, код самодокументируется.
Вывод: Constraints с extends и вывод через infer дают строгую типизацию factory-функций и конфигурационных объектов без потери читаемости и с проверками на уровне компиляции. | 1 823 |
| 14 | Попросили Claude создать WCAG-доступный DataPicker на React и потратили 3 дня на доработки
Выбор даты кажется небольшой задачей в UI, пока не попробуешь сделать его по-настоящему WCAG-доступным. Нам понадобился настраиваемый DataPicker на React для процесса записи на прием к врачу, где пользователи, работающие с keyboard navigation, и люди, использующие screen reader’ы, должны были выбрать дату без лишних затруднений.
Claude сделал нам хорошую первую версию: структуру компонента, ARIA-атрибуты, базовую keyboard navigation и логику календаря. На первый взгляд результат выглядел почти готовым. Затем мы запустили NVDA, VoiceOver и протестировали сценарий keyboard navigation. Фокус выходил за пределы диалогового окна; некоторые даты озвучивались неверно; переключение между месяцами сопровождалось слишком тихим звуком; нажатие клавиши «Esc» закрывало календарь, но оставляло пользователя без контекста; режим высокой контрастности Windows нарушал отображение выбранного состояния. Код выглядел нормально, но UX оставлял желать лучшего.
В этой статье мы рассмотрим реальную работу, стоящую за WCAG-доступным DataPicker'ом: где AI сэкономил нам время; где он не справился; как нам помог WAI-ARIA APG ; какие детали нам пришлось исправлять вручную и почему доступность нельзя проверить, просто прочитав сгенерированный код.
Читать далее | 1 |
| 15 | Два способа создания доступного DatePicker'а с помощью AI: 80/20 или системное проектирование
DatePicker потребовался на React и TypeScript с корректной работой keyboard navigation, screen reader'а, управляемым состоянием и проверками доступности. Первый способ — дать AI четкий запрос, получить 80% кода, остальное доработать руками. Модель генерирует структуру календаря, атрибуты ARIA, базовую keyboard navigation и логику работы с датами. Затем начинаются проблемы: поведение фокуса становится нестабильным, возникают конфликты обработчиков событий, озвучивание screen reader'ами требует тестирования, небольшое изменение в логике может нарушить календарь.
Второй способ — системное проектирование с AI-агентом. Включает PRD, декомпозицию задач, правила агента, внешнюю верификацию, Vitest, Playwright, сборку Vite, проверки типов и строгий цикл: агент не может двигаться дальше, пока не пройден текущий шаг. Далее разбирается, в чем AI действительно помог, где начал сбиваться, почему одного большого запроса недостаточно, как The Verifier изменил процесс и почему задача инженера сводится к контролю над замыслом, архитектурой, контрактами и стоимостью изменений.
Читать далее на Habr | 1 978 |
| 16 | React 19: ErrorBoundary с типизацией, error.cause и строгое логирование
Границы ошибок в React долго выглядели как костыль для асинхронщины: раньше ErrorBoundary отлавливал только синхронные ошибки, а запросы с then/catch просто пролетали мимо. В 19 версии это починили через use и новые хуки, но типизация все равно остается местом, где можно наступить на грабли в production.
Интерфейс и цепочка ошибок
Интерфейс ErrorBoundaryState с Error | null, getDerivedStateFromError ловит ошибку, componentDidCatch отправляет в лог. Но что действительно спасает в реальных кейсах — это error.cause из ES2022. Когда fetch падает с 401, ты пробрасываешь не строку, а объект:
* error.cause?.status для проверки статуса
* error.cause?.message для текста
interface AppError extends Error {
cause?: {
status: number;
message: string;
};
}
Production: сужение и безопасность
Передавать полный stack клиенту — плохая идея. Там пути файлов и внутренние адреса. Мы обрезаем первые 200 символов, если ошибка некастомная, и никогда не светим причину наружу: error.cause только во внутренний логгер.
Схема логирования через instanceof:
* если error instanceof AppError — пишем структурированно с типом, причиной и стеком
* если просто Error — обрезаем стек, кидаем предупреждение
* если вообще не Error — логируем как строку и показываем generic fallback
Типичная ошибка
instanceof не работает, если ошибка прилетела из другого iframe или realm. Там error.cause может быть сериализованным объектом, и придется проверять по полям вручную. Встречал такое при интеграции с микросервисными виджетами. Еще один момент — не засовывай всю логику в componentDidCatch: вынеси ее в отдельную функцию handleFatalError с возвратом never и переиспользуй, иначе при рефакторинге придется переписывать каждый Boundary.
Вывод: Типизированный ErrorBoundary с error.cause и строгим логированием через instanceof — это надежный паттерн для обработки асинхронных ошибок, который дает консистентность и безопасность в production.
Источники: React docs про Error Boundaries, MDN про Error.cause, React 19 release notes про use() hook и async errors. | 2 085 |
| 17 | Типизация ErrorBoundary в React 19: асинхронные ошибки, error.cause и production-логирование
ErrorBoundary долго был слабым звеном — он не ловил ошибки из промисов, useEffect или setTimeout. React 19 не меняет классовый подход, но появление error.cause и правильная типизация через instanceof делают обработку асинхронных ошибок предсказуемой и безопасной.
Почему any в state — ошибка
Состояние границы должно быть строго типизировано: Error | null. В getDerivedStateFromError проверяй cause через instanceof Error, не через truthy check. Это исключает мусор из продакшена, где cause может быть строкой, числом или undefined.
Production-кейс с fetch
При HTTP 500 выбрасывай:
throw new Error('HTTP error', { cause: res.status })
В границе проверяй:
if (error.cause instanceof Error) {
// Логируем полный стек
} else if (typeof error.cause === 'number') {
// Игнорируем, если статус < 500
}
Это позволяет не слать в Sentry временные сетевые сбои.
Типизация cause и instanceof
Используй instanceof для разграничения классов ошибок — HTTPError, NetworkError, ValidationError. Так ты контролируешь, что идёт в Sentry, а что — только в консоль. Без этого логгер захлебнётся шумом.
Вывод:
Типизированный ErrorBoundary с error.cause и instanceof превращает асинхронные ошибки из невидимых багов в управляемые инциденты. | 2 135 |
| 18 | Как вывести типы контекстов во вложенных layout без ручных интерфейсов в React Router v7
Если вы используете TanStack Router (React Router v7) с вложенными layout, то наверняка сталкивались с ситуацией, когда родительский layout прокидывает loader-контекст, а дочерний компонент вынужден вручную объявлять интерфейс и кастить. Типовая ошибка — забыть обновить интерфейс при изменении структуры данных, что ведет к багам на проде.
Как работает типовая цепочка
Система типов строится вокруг дженериков createRoute. Каждый роут наследует контекст от родителя через getParentRoute(). TypeScript автоматически мержит результаты loader родительского и текущего роута:
const rootRoute = createRootRoute()({
loader: () => ({ user: 'John' })
});
const layoutRoute = createRoute({
getParentRoute: () => rootRoute,
loader: ({ context }) => ({
theme: context.user === 'John' ? 'dark' : 'light'
})
});
const pageRoute = createRoute({
getParentRoute: () => layoutRoute,
loader: ({ context }) => {
// context: { user: string, theme: string }
return context.theme;
}
});
Ни одного явного интерфейса или каста. Система выводит тип контекста из цепочки родительских роутов.
Production-кейс: аутентификация с правами
Практический пример: вложенный layout с авторизацией. Родитель кладет user, дочерний — permissions:
const authLayout = createRoute({
getParentRoute: () => rootRoute,
loader: () => ({ user: fetchUser() })
});
const adminLayout = createRoute({
getParentRoute: () => authLayout,
loader: ({ context }) => ({
permissions: fetchPermissions(context.user.id)
})
});
Страница получает { user, permissions } без ручного объявления. Если переименовать user в currentUser — TypeScript подсветит все места, где используется старый ключ.
Типичная ошибка и trade-off
Ошибка: ручное объявление interface IContext = { user: string } и последующий as any при несоответствии. Это ломает всю type-safety и ведет к runtime-ошибкам при изменении данных.
Trade-off: полагаться на вывод типов чуть сложнее читать в IDE, чем явные интерфейсы, но выигрыш в надежности при рефакторинге — код сам документирует контракты, и компилятор ловит несоответствия.
Вывод: Используйте автоматический вывод типов контекстов через цепочку getParentRoute, чтобы избавиться от ручных интерфейсов и кастингов, повысив надежность и упростив рефакторинг в production-коде. | 1 979 |
| 19 | Signal vs Observable: почему ваш граф зависимостей взрывается в production
Когда в корзине интернет-магазина total = price * qty, затем скидка от суммы, затем налог — Observable (RxJS) на каждое изменение price пересчитывает все подписки, даже если данные не изменились. Разработчики навешивают distinctUntilChanged на каждую цепочку, и через месяц граф превращается в лапшу.
Как работают Signal
Signal (SolidJS, Vue 3.4+, Preact Signals, Svelte 5) делает вычисления ленивыми. Он отслеживает, какие сигналы реально читаются в computed, а не подписывается на всё подряд. Два сигнала, зависящих от одного источника, не подписываются дважды. При изменении источника пересчитываются только те, кто на него ссылается, а не всё дерево.
Пример на псевдокоде Solid:
count = signal(1)
price = signal(100)
total = computed(() => count() * price())
discount = computed(() => total() > 500 ? 0.1 : 0)
final = computed(() => total() * (1 - discount()))
При изменении count пересчитываются total и final. Discount пересчитывается только если total пересечет порог. В RxJS через combineLatest discount будет пересчитываться на каждое изменение count и price, даже если total не изменился. Меньше кода — больше бесполезной работы.
Когда Observable все еще нужен
Observable (RxJS) хорош для асинхронных цепочек: debounce, switchMap, WebSocket. Это декларативные потоки, где важны трансформации во времени. Но при сотне зависимостей начинается цирк: каждый чих триггерит пересчёт, дебажить dependency hell — отдельный квест. Типичная ошибка — использовать Observable для синхронного графа зависимостей, где сигналы дают ровно то же самое без лишних пересчётов.
Практический совет
Если у вас в проекте уже RxJS — не переписывайте всё. Для нового фича с interdependent состояниями (формы, редакторы, дашборды) закладывайтесь на сигналы. В продакшене с сотнями зависимостей сигналы дают на 30-50% меньше бесполезных перерисовок и упрощают отладку: граф зависимостей прозрачен, а не взрывается на каждой итерации.
Вывод: Signal — для синхронных графов с предсказуемыми изменениями, Observable — для event-driven асинхронных потоков, и выбор между ними — это trade-off между производительностью вычислений и гибкостью трансформаций. | 2 049 |
| 20 | 🤣 Общаться о синтаксисе теперь тоже можно через агентов
✖️ xCode Journal | 2 068 |
Уже доступно! Исследование Telegram 2025 — ключевые инсайты года 
