Vite, структура проекта, строгий tsconfig, ESLint, React Compiler, зависимости, окружение и воспроизводимая сборка.
Инструментарий должен быстро сообщать об ошибке и одинаково работать на ноутбуке, в CI и в production-сборке.
Вы соберёте минимальный проект без Create React App, разделите проверки типов и трансформацию, включите правила React и зафиксируете воспроизводимый quality gate.
До начала достаточно основы ES-модулей, package.json и командной строки. В конце должен появиться не конспект, а проект, проходящий typecheck, lint, unit-тесты и production build одной документированной последовательностью.
Для изучения клиентского React удобно создать Vite-проект из react-ts шаблона. Для нового production-приложения React рекомендует сначала оценить framework, потому что маршрутизация, data fetching и rendering strategy уже являются частью архитектуры. Учебный Vite-стенд нужен, чтобы увидеть React без скрытой серверной магии.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
npm create vite@latest react-lab -- --template react-ts
cd react-lab
npm install
npm run devКоманда создаёт ESM-проект с JSX transform и TypeScript, но не принимает за вас решения о routing, data cache и SSR.
Рабочая проверка. Запишите версии Node и package manager, команды запуска и production build в README.
Типичная поломка. Начать новый курс с Create React App или скрыть все механизмы внутри большого framework до изучения render model.
Сборщик может убрать типы и получить JavaScript даже при ошибках модели. Поэтому tsc --noEmit запускается отдельно. Для React-проекта важны strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, useUnknownInCatchVariables, современный JSX transform и module resolution, совместимый со сборщиком. Каждый дополнительный флаг вводится вместе с исправлением кода, а не с массовым suppression.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"useUnknownInCatchVariables": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"noEmit": true
}
}noEmit отделяет проверку от сборщика, а react-jsx использует автоматический JSX runtime без обязательного import React ради JSX.
Рабочая проверка. Добавьте намеренно небезопасный доступ по индексу и убедитесь, что CI его отклоняет.
Типичная поломка. Считать наличие .tsx доказательством строгой типизации.
Клиентская сборка превращает разрешённые env-переменные в значения JavaScript. Всё, что попало в браузерный bundle, доступно пользователю независимо от имени. В Vite по умолчанию клиент видит только префикс VITE_; это фильтр экспорта, а не секретное хранилище. Форму import.meta.env нужно типизировать и проверять значения при старте.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
}
const apiBase = new URL(import.meta.env.VITE_API_BASE_URL);Аннотация улучшает использование, но пустая или неверная строка всё равно требует runtime-проверки, например через URL или схему.
Рабочая проверка. Сломайте URL в .env и добейтесь ранней понятной ошибки вместо запросов на undefined/path.
Типичная поломка. Положить API secret в VITE_API_SECRET, потому что имя содержит слово secret.
Компилятор типов не видит порядок вызова Hooks, небезопасное чтение refs во время render или setState-петлю как типовую ошибку. Эти инварианты проверяет eslint-plugin-react-hooks. Актуальные compiler-powered rules входят в рекомендуемые presets и полезны даже до включения трансформации Compiler.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
import { defineConfig } from 'eslint/config';
import reactHooks from 'eslint-plugin-react-hooks';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);Правила анализируют семантические ограничения React. Отключение конкретного предупреждения требует объяснить инвариант, а не просто очистить CI.
Рабочая проверка. Создайте условный вызов Hook и setState во время render, затем изучите разные диагностики.
Типичная поломка. Оставить только formatter и ожидать, что он найдёт нарушение Rules of Hooks.
React Compiler 1.0 стабилен и автоматически мемоизирует компоненты и Hooks на основе анализа данных и мутаций. Для нового кода предпочтительно писать чистый декларативный React и позволять Compiler выбрать границы. useMemo, useCallback и memo остаются escape hatches, но не должны рассыпаться по коду без измерения или семантической потребности в стабильной ссылке.
Рабочая проверка. Включите Compiler в одну область, проверьте совместимость lint-правилами и сравните поведение тестов до и после.
Типичная поломка. Механически удалить всю ручную мемоизацию из старого проекта сразу после установки Compiler.
package.json задаёт допустимые диапазоны, а lockfile фиксирует разрешённый граф зависимостей и integrity. CI должен использовать команду чистой установки (npm ci для npm), не изменяющую lockfile. Версии runtime и package manager также фиксируются. Dependabot-подобное обновление — отдельное изменение с тестами, а не побочный эффект любой сборки.
Рабочая проверка. Удалите node_modules, выполните чистую установку по lockfile и сравните дерево зависимостей в двух запусках.
Типичная поломка. Игнорировать lockfile для приложения и получать разные транзитивные версии в CI.
ES-модули формируют статический граф, который сборщик анализирует для tree shaking и чанков. Динамический import() создаёт асинхронную границу, если путь анализируем. Разбиение оправдано пользовательским маршрутом или редкой тяжёлой возможностью, а не целью получить максимальное число файлов. Проверять нужно загруженные байты и задержку взаимодействия.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
import { lazy, Suspense } from 'react';
const ReportEditor = lazy(() => import('./ReportEditor.tsx'));
export function Reports() {
return <Suspense fallback={<p>Загрузка редактора…</p>}><ReportEditor /></Suspense>;
}lazy ждёт default export модуля, а Suspense задаёт видимое состояние загрузки чанка. Ошибку загрузки обрабатывает error boundary.
Рабочая проверка. Сравните initial bundle и network waterfall до и после выделения редко открываемого редактора.
Типичная поломка. Лениво загружать маленькую кнопку, создавая дополнительный round trip на основном пути.
Один скрипт верхнего уровня должен объединять неизменяющие проверки: typecheck, lint, unit/integration tests и build. E2E может быть отдельным более дорогим gate. Ошибка должна указывать слой: тип, правило React, поведение или сборка. Кэш CI ускоряет повтор, но ключуется lockfile и конфигурацией, иначе он маскирует drift.
Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.
{
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings=0",
"test": "vitest run",
"build": "vite build",
"check": "npm run typecheck && npm run lint && npm run test && npm run build"
}
}Последовательность сначала запускает дешёвые диагностические проверки, затем более дорогую production-сборку.
Рабочая проверка. Внесите по одной ошибке каждого класса и убедитесь, что сообщение ведёт к первопричине.
Типичная поломка. Склеить команды через подавление exit code, чтобы pipeline всегда был зелёным.
Настройте приложение каталога на шаблоне Vite react-ts, включите строгие флаги TypeScript, flat ESLint config с правилами Hooks и Compiler, типизированные переменные окружения и CI-команду npm run check.
Критерий завершения: чистый checkout устанавливается по lockfile и падает до сборки при нарушении типа, правил Hooks или теста. Сначала зафиксируйте наблюдаемое поведение тестом или измерением, затем меняйте реализацию.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.
Далее: JSX и типизированные компоненты