Vite — быстрая сборка фронтенда

Vite («быстрый» по-французски) — сборщик, который сделал работу разработчиков мгновенной. Вместо предварительной сборки всего проекта он отдаёт код по мере надобности и перезагружает только изменившееся — это и есть HMR за миллисекунды.

Уровень: средний 17 разделов ⏱ 2–3 часа

1. Что такое Vite

Vite — инструмент сборки и dev-сервер для современных веб-проектов. Его идеи:

Vite — преемник Webpack в новых проектах (курс Webpack). Старые проекты переезжают медленно, поэтому знание обоих полезно.

2. Создание проекта

npm create vite@latest my-app
# select framework: Vanilla / Vue / React / Svelte / Solid / Qwik
# select variant: TypeScript или JavaScript

cd my-app
npm install
npm run dev

Структура минимального проекта:

my-app/
├── index.html       # точка входа (корень проекта)
├── src/
│   ├── main.ts
│   └── App.tsx
├── vite.config.ts   # конфигурация Vite
└── package.json

3. Dev-сервер и HMR

Команда npm run dev поднимает сервер на http://localhost:5173. Что происходит:

npm run dev -- --port 3000    # сменить порт
npm run dev -- --host          # открыть в локальной сети
При чистом НМР-обновлении состояние компонента (например, введённый текст в форме) может сохраняться — но только если фреймворк поддерживает fast refresh. Для сторонних файлов полный reload.

4. Конфигурация и плагины

Главный файл настройки — vite.config.ts:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],          // для React-проекта
  server: {
    port: 3000,
    proxy: { "/api": "http://localhost:8080" }
  },
  build: {
    outDir: "dist",
    sourcemap: false
  }
});

Плагины расширяют Vite: TypeScript-транспиляция, CSS-инъекция, генерация PWA (vite-plugin-pwa) и многое другое.

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

Значения в файле .env попадают в код только с префиксом VITE_:

# .env
VITE_API_URL=https://api.example.com
VITE_APP_VERSION=1.2.0
// в коде
const api = import.meta.env.VITE_API_URL;
const isDev = import.meta.env.DEV;    // true в development
Переменные без префикса VITE_ не попадают в клиентский бандл — это защита от случайной утечки секретов. Секреты сервера храни в .env.local и не коммить.

6. Импорт ресурсов и CSS

Vite нативно понимает импорты картинок, шрифтов, SVG и CSS:

import logo from "./logo.svg";          // URL ресурса, файл хэшируется
import "./styles.css";                  // стили попадут в бандл
import styles from "./m.css";           // CSS Modules: styles.title

<img src={logo} alt="логотип" />

Для больших SVG можно подключить vite-svg-loader и использовать как компонент Vue/React.

7. Продакшен-сборка

npm run build
# создаст dist/: index.html + минифицированные JS/CSS
npm run preview    # локально посмотреть собранное

Что делает build:

8. Оптимизация и code splitting

Тяжёлые зависимости выносим в отдельные чанки через manualChunks, а страницы грузим лениво через динамический импорт:

build: {
  rollupOptions: {
    output: {
      manualChunks: {
        vendor: ["vue", "element-plus"]
      }
    }
  }
}
// Ленивая загрузка страницы в Vue Router
const Product = () => import("../views/Product.vue");

// ленивая загрузка в React
const Product = lazy(() => import("./views/Product"));
Code splitting уменьшает стартовый бандл: пользователь получает только то, что нужно для первой страницы, а остальное догружается по мере навигации.

9. Vite + Vue, React, TypeScript

ШаблонКомандаКурс
Vue + TS--template vue-tsVue 3
React + TS--template react-tsReact
Vanilla + TS--template vanilla-tsJavaScript

В шаблонах уже настроены ESLint (курс), TypeScript (курс) и HMR. Для тестов используй Vitest — его создал тот же автор, что и Vite (курс Vitest).

10. Proxy и работа с API

На время разработки бэкенд отделён от фронтенда. Проксируй запросы на реальный API, чтобы не ловить CORS:

server: {
  proxy: {
    "/api": {
      target: "http://localhost:8000",
      changeOrigin: true,
      rewrite: (p) => p.replace(/^\/api/, "")
    }
  }
}
// в коде: fetch("/api/products") → http://localhost:8000/products
const res = await fetch("/api/products");
Proxy работает только в dev. В продакшене фронтенд и бэкенд обычно раздают за одним доменом (nginx, порт-прокси) — развитие в курсе REST API и деплой в Docker.

11. Режим библиотеки (lib mode)

Vite собирает не только приложения, но и библиотеки: UI-компоненты, SDK, утилиты. В конфиге задаются точка входа и форматы вывода:

build: {
  lib: {
    entry: "src/index.ts",
    name: "MyLib",
    formats: ["es", "cjs"]
  },
  rollupOptions: {
    external: ["vue"],     // vue не попадёт в бандл
    output: { globals: { vue: "Vue" } }
  }
}

В package.json укажи точки входа:

{
  "main": "./dist/my-lib.cjs",
  "module": "./dist/my-lib.js",
  "exports": { ".": "./dist/my-lib.js" }
}
Для React/Vue-библиотек выноси зависимости во external и добавляй их же в peerDependencies — иначе в приложении окажется два экземпляра фреймворка разных версий.

12. Vite и PWA

Плагин vite-plugin-pwa подключает сервис-воркер, офлайн-кэш и установку приложения буквально одной строкой:

import { VitePWA } from "vite-plugin-pwa";
export default defineConfig({
  plugins: [
    VitePWA({
      registerType: "autoUpdate",
      manifest: {
        name: "Мой магазин",
        short_name: "Shop",
        theme_color: "#2563eb",
        icons: [{ src: "/icon.png", sizes: "192x192", type: "image/png" }]
      }
    })
  ]
});
PWA требует HTTPS и реального прод-домена: на localhost проверить можно, но без выпущенного сертификата браузер откажет в сервис-воркере.

13. CI, деплой и аудит

Продакшен-сборку удобно гонять в непрерывной интеграции. Пример пайплайна на GitHub Actions (курс здесь):

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with: { node-version: 20 }
  - run: npm ci
  - run: npm run test
  - run: npm run build
  - uses: actions/upload-pages-artifact@v3
    with: { path: "dist" }

Перед выкладкой прогоняй в том же джобе тесты Vitest и линт ESLint, а после сборки смотри отчёт о чанках и скорость Lighthouse.

Держи строгий tsconfig: когда npm run build падает на ошибках типов, баги ловятся в CI, а не на проде.

14. SSR и SSG: серверный рендер и статика

Vite собирает не только SPA. Для серверного рендера есть отдельная команда сборки, а для генерации статических страниц — связка vite-ssg:

vite build --ssr src/server.ts   # точка входа для сервера
npm i -D vite-ssg
import { ViteSSG } from "vite-ssg/vue"
ViteSSG({ routes, renderPages })  # HTML генерится на этапе сборки
SSG — простейший способ получить SEO и быстрый первый кадр без управления сервером: подойдёт для лендингов и документации.

15. Инспекция модулей и отладка сборки

Когда неясно, почему модуль попал в бандл или трансформировался странно — установи vite-plugin-inspect. Он показывает промежуточные состояния кода: исходник → транспилированный → минифицированный, и какие плагины что изменили.

npm i -D vite-plugin-inspect
plugins: [inspect()]
// в dev открой localhost:5173/__inspect
// time — сколько ждал шаг, transform — изменения кода

На вопросы «почему сборка долгая» отвечает профилирование vite build --profile, а размер чанков покажет визуализатор бандла:

npm i -D rollup-plugin-visualizer
visualizer({ gzipSize: true })  // карта бандла в браузере
Сначала «вскрывай» бандл — потом оптимизируй. Иначе не поймёшь, какая правка реально уменьшила килобайты, а что осталось косметикой.

16. Монорепозитории и рабочее окружение

Vite дружит с workspace-монорепами (pnpm/npm workspaces): несколько пакетов собираются в одном конфиге, а зависимости линкуются автоматически.

pnpm-workspace.yaml
packages: [ "apps/*", "packages/*" ]

// пакет components импортируется по имени пакета
import { Button } from "@ui/button"
Монорепа требует согласованных версий зависимостей: пакет, собранный с одним React, ломается при линковке с другим. Выноси общие зависимости в корневой package.json.

17. Миграция с Webpack и чек-лист запуска

Переезд с Webpack на Vite проходит в три шага:

  1. перенеси entry и loaders в plugins (babel → @vitejs/plugin-legacy, sass-loader → обработка из коробки);
  2. замени DefinePlugin на import.meta.env, а webpack-алиасы — на resolve.alias;
  3. проверь полифиллы и версии зависимостей.
resolve: { alias: { "@": "/src" } },
define: {},   // вместо DefinePlugin
optimizeDeps: { include: ["lodash"] }

Финальный чек-лист прод-запуска:

Сначала мигрируй один страничный модуль и сравни скорость сборки и размер бандла — цифры убедят команду быстрее аргументов.

18. import.meta.glob: модули по шаблону

Вместо десятка строк import v1 from "@/views/Page1.vue" можно импортировать сразу всё, что попадает под маску:

const lazyPages = import.meta.glob("../views/*.vue");
// { "../views/Home.vue": () => import("../views/Home.vue"), ... }
const eagerPages = import.meta.glob("../utils/*.ts", { eager: true });
const pages = import.meta.glob("../views/*.vue", { eager: true });
const routes = Object.entries(pages).map(([path, mod]) => ({
  path: "/" + path.split("/").pop().replace(/\.vue$/, ""),
  component: mod.default
}));
Маска ?raw вернёт содержимое файла строкой (SVG, markdown), а ?url — URL ресурса без хэширования. Это запасной выход для «нестандартных» файлов.

19. Собственный плагин: виртуальные модули

Плагин Vite — это объект с именем и хуками. Самые частые хуки: config (правка конфига), transform (меняет код модуля перед отдачей) и resolveId/load (виртуальные модули):

export default function myPlugin() {
  return {
    name: "my-plugin",
    transform(code, id) {
      if (id.endsWith(".tpl")) {
        return `export default ${JSON.stringify(code)}`;
      }
    },
    resolveId(id) {
      if (id === "virtual:readme") return "\0virtual:readme";
    },
    load(id) {
      if (id === "\0virtual:readme") return `export default "документ";`;
    }
  };
}
Держи transform лёгким и без сайд-эффектов: он вызывается на каждый модуль при каждой инвалидации, и тяжёлый плагин мгновенно замедлит dev-сервер.
Готовый скелет — vite-plugin: тип для defineConfig подскажет все хуки прямо в редакторе.

20. DevTools и интеграция с редактором

Быстрый dev-сервер — половина дела; вторая половина — отладка. В dev-режиме Vite отдаёт исходники как есть, поэтому переходить к коду можно прямо из браузера:

npm i -D @vitejs/plugin-vue-devtools
// vite.config.ts
import vue from "@vitejs/plugin-vue";
import vueDevTools from "@vitejs/plugin-vue-devtools";
plugins: [vue(), vueDevTools()]
npm run dev -- --debug      # лог преобразований и resolve каждого модуля
npm run dev -- --host 0.0.0.0   # доступ с телефона и планшета по LAN
Когда «переход к определению» не работает — обнови .vscode/launch.json или запусти vite через отладчик типа node: точка останова встанет прямо в исходнике.

21. Подводные камни Node и кэша зависимостей

«У меня не собирается, а у коллеги да» — частая жалоба в Vite-проектах. Разница почти всегда в версиях или кэше:

СимптомПричинаРешение
Ошибка ESM-CJS при импортебиблиотека без поддержки ESMoptimizeDeps.include или обновление пакета
Старые результаты после обновлениякэш node_modules/.vitevite --force
Сервер «роняет» на каждом файленесовместимая версия Nodeсверка с engines в package.json
Чужие хэши в бандленесовпадение lock-файлов в CInpm ci в пайплайне
// package.json
"engines": { "node": ">=20" },
"packageManager": "pnpm@9"
Закрепляй версию Node в CI и локально через .nvmrc или volta, иначе «работает у меня» будет преследовать команду до конца проекта.
Перед долгим поиском бага в dev — rm -rf node_modules/.vite и перезапуск. В большинстве странных фризов виноват протухший deps-кэш.

Что дальше

Размести свой проект на GitHub и деплой на Pages/Vercel, стили — в Sass. Качество кода проверит ESLint, а скорость загрузки — аудит Lighthouse.

← Вернуться к курсам