Frontender's notes [ru]
Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами. Личный блог автора - @just_genych По вопросам рекламы или разработки - @g_abashkin
Mostrar más📈 Análisis del canal de Telegram Frontender's notes [ru]
El canal Frontender's notes [ru] (@frontendnoteschannel_ru) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 32 236 suscriptores, ocupando la posición 4 192 en la categoría Tecnologías y Aplicaciones y el puesto 20 089 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 32 236 suscriptores.
Según los últimos datos del 03 julio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -332, y en las últimas 24 horas de -13, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 7.14%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 4.60% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 2 303 visualizaciones. En el primer día suele acumular 1 483 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 10.
- Intereses temáticos: El contenido se centra en temas clave como браузер, api, css, интерфейс, загрузка.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами.
Личный блог автора - @just_genych
По вопросам рекламы или разработки - @g_abashkin”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 04 julio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
Carga de datos en curso...
| Fecha | Crecimiento de Suscriptores | Menciones | Canales | |
| 04 julio | +1 | |||
| 03 julio | +4 | |||
| 02 julio | 0 | |||
| 01 julio | 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 |
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
