Перейти к содержимому
Запуск проекта и базовая конфигурация

Запуск проекта и базовая конфигурация

Базовый обзор структуры проекта

После выполнения предыдущей лабораторной у вас сгенерировался стартовый шаблон. Прежде чем переходить к написанию собственных компонентов, давайте разберёмся, что именно лежит в проекте и зачем нужен каждый файл.

      • vite.svg — статика, отдаётся как есть
        • react.svg — пример ассета в исходниках
      • App.css
      • App.tsx
      • index.css
      • main.tsx — точка входа React-приложения
      • vite-env.d.ts — типы для import.meta/env и импорта ассетов
    • index.html — входная HTML-страница (entrypoint)
    • package.json — скрипты, зависимости
    • tsconfig.json — базовый TS-конфиг
    • tsconfig.app.json — TS-конфиг для клиентского кода
    • tsconfig.node.json — TS-конфиг для Node-окружения
    • vite.config.ts — конфигурация Vite + плагин React
    • .gitignore
    • README.md
.gitattributes и переводы строк. Чтобы не получить хаос из CRLF/LF между Windows и Linux, добавьте .gitattributes с нормализацией строк (* text=auto). Подробности — в документации GitHub.

В Vite основной точкой входа служит файл index.html. Браузер получает эту страницу, а Vite уже инжектит туда ваши скрипты и стили. Во время разработки именно он обслуживается dev-сервером, здесь подключается src/main.tsx и создаётся контейнер для React.

Алиас @ на src

Чтобы не писать длинные относительные пути вроде ../../components/Button (обращение к компоненту «Кнопка», который находится в директории с компонентами), заведём алиас @ на src. Для этого надо будет произвести правки в файлах vite.config.ts (файл с настройками Vite) и tsconfig.app.json (файл настройки окружения TypeScript для файлов внутри директории src).

Для начала применим необходимые правки внутри vite.config.ts:

vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // теперь можно import '@/components/Button'
    },
  },
});

Нюанс. Чтобы не выдавало ошибок, не забудьте поставить типы Node при помощи команды:

npm i -D @types/node

Но этого будет недостаточно: при проверке синтаксиса TypeScript будет выдавать ошибки, т.к. он такого синтаксиса на текущий момент ещё не знает. Чтобы у нас не выдавались ошибки при резолве импортов, необходимо дополнить tsconfig.app.json (а не перезаписать целиком — внутри compilerOptions уже есть набор опций от шаблона Vite, мы только добавляем baseUrl и paths):

tsconfig.app.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

В данном случае baseUrl и paths — это стандартный механизм для резолва импортов.

У нас в директории осталось ещё два файла — tsconfig.json и tsconfig.node.json, для чего тогда нужны они? На самом деле в современных проектах часто используется принцип разделения конфигурации TS:

  • tsconfig.json — базовый;
  • tsconfig.app.json — настройки для клиентского кода в src/;
  • tsconfig.node.json — настройки для Node-окружения (например, для vite.config.ts).

Переменные окружения

Далее разберёмся с переменными окружения, что это такое и для чего это нужно. Переменная окружения — это значение, которое передаётся снаружи (машина разработчика, CI, сервер), чтобы не хардкодить чувствительные или зависящие от среды аргументы: URL API, флаги, ключи тестовых сервисов.

Во фронтенде это всегда превращается в компромисс: всё, что попадёт в бандл, видно в браузере. Поэтому в Vite действует правило: к клиентскому коду попадают только переменные с префиксом VITE_ (например, VITE_API_URL). Их читают через import.meta.env (подробнее про этот механизм можно прочитать в официальной документации Vite):

.env
VITE_APP_TITLE=Room & Assets
VITE_API_URL=/api
src/config.ts
export const APP_TITLE = import.meta.env.VITE_APP_TITLE;
export const API_URL   = import.meta.env.VITE_API_URL;
Альтернатива: runtime-конфигурация без пересборки образа

Ещё один способ, с которым вы можете иногда встретиться — генерация runtime-конфигурации, которая уже в свою очередь считывается собранным Vite-образом. Благодаря такому подходу отпадает необходимость пересборки всего образа фронтенда, если необходимо изменить одно из значений.

public/config.js
window.__APP_CONFIG__ = {
  captchaEnabled: "true",
  captchaSiteKey: "ysc1_Vf415QomO7JMtVYTnUVkqXdfo4F2hBaeOxjECRch3d45b583",
  cookiePreferencesEnabled: "false",
  gaId: "G-XXXXXXXXXX",
  ymId:  "987654321",
  webHost: "caplag.ru",
  baseUrl: "${PUBLIC_BASE_URL}"
};

У вас генерируется открытый конфиг-файл (либо от бэкенда, либо при помощи механизма templates и ENVSUBST у NGINX), который уже передаётся на чтение самому фронтенду.

Vite поддерживает режимы (development, production, свои кастомные), и под каждый можно положить файл вида .env.production или .env.staging. Выбор делается флагом --mode у команды, например: vite build --mode staging. Для строгой типизации import.meta.env можно завести src/vite-env.d.ts и описать свои ключи.

package.json — манифест проекта

Базовая конфигурация в каком-то виде настроена, но как это всё дело запустить? Здесь обращаем внимание на package.json и его товарища package-lock.json. Для чего они нужны?

package.json — это центральный конфигурационный файл проекта, не менее важный, чем vite.config.ts или tsconfig. Это манифест проекта, который содержит: имя, версию, список зависимостей, команды (скрипты), технические флаги и метаданные. Файл обязан быть валидным JSON — без комментариев и лишних запятых. NPM и Node читают его, чтобы понять, что ставить, как запускать, и какие правила окружения соблюдать.

После генерации проекта с предложенным шаблоном его содержимое у вас (скорее всего) будет выглядеть следующим образом:

package.json
{
  "name": "room-assets",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "engines": {
    "node": ">=20.19 <21 || >=22.12"
  },
  "packageManager": "npm@10.9.2",
  "dependencies": {
    "react": "^19.1.1",
    "react-dom": "^19.1.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.33.0",
    "@types/react": "^19.1.10",
    "@types/react-dom": "^19.1.7",
    "@vitejs/plugin-react": "^5.0.0",
    "eslint": "^9.33.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^16.3.0",
    "typescript": "~5.8.3",
    "typescript-eslint": "^8.39.1",
    "vite": "^7.1.2"
  }
}

Что за что отвечает

  • private: true — защита от случайной публикации пакета в общий реестр. Для приложений это принято держать включённым.
  • type: "module" — говорит Node.js, что .js внутри проекта интерпретируются как ES-модули (а не CommonJS). Это влияет на import/export и расширения файлов.
  • engines.node — фиксирует минимальный Node. С Vite 7 требуется Node 20.19+ или 22.12+ — запишем это в явном виде. В CI мы будем использовать Node 20, а в Docker — node:24-alpine (текущая LTS-линия на момент написания), поэтому диапазон допускает обе версии. Если хотите запретить Node 24, оставьте поле как есть и переключите Dockerfile-образы на node:22-alpine.
  • packageManager — закрепление выбора конкретного пакетного менеджера (npm/pnpm/yarn) с точной версией. Этим занимается Corepack, входящий в Node: он читает поле и подтягивает нужный менеджер, чтобы локально и в CI все использовали один и тот же инструмент.
Версии в примере условные — у вас они могут немного отличаться (но вряд ли сильно, потому что проект создан недавно).

Скрипты npm run <имя>

В секции scripts мы видим короткие имена командам. Запускаются они в консоли с использованием команды npm run <имя>. Список стандартных команд отвечает за:

  • dev — запускает Vite-dev-server для разработки: быстрый старт и HMR (горячая замена модулей без перезагрузки страницы). Это не урезанная имитация продакшена, а максимально комфортная среда для правки кода.
  • build — собирает приложение для продакшена (директория dist/). Также в созданном по умолчанию файле мы видим, что подключён type-check (tsc -b) перед сборкой — это хорошая привычка, поскольку Vite сам по себе типы не проверяет.
  • preview — поднимает локальный сервер, который отдаёт уже собранный dist/. Удобно проверить поведение приложения в продакшн-сборке, если для этого не используется отдельный хостинг.

dependencies vs devDependencies

Также, я думаю, возникает логичный вопрос: чем отличаются dependencies и devDependencies (по крайней мере, надеюсь, что возникает)? Рабочие библиотеки приложения (React, date-fns и т.п.) попадают в dependencies. Инструменты сборки, типы, линтеры — в devDependencies.

Возле package.json обычно находится ещё один файл — package-lock.json. Он фиксирует точные версии всей зависимой цепочки, чтобы при сборке в различных средах не возникало проблем, что у одного работает, а у другого нет. Этот файл нужно коммитить в репозиторий. В CI полезно использовать npm ci — тогда при установке модулей он будет строго следовать lock-файлу, находящемуся в репозитории.

Теперь смело запускаем проект npm run dev и переходим по ссылке из консоли на стартовую страницу. В следующей лабораторной мы возьмём этот шаблон за основу и начнём строить собственную архитектуру и компоненты.