ДокументацияИнструментыVite: быстрая сборка, конфигурация, плагины
Средний 15 мин чтения

Vite: быстрая сборка, конфигурация, плагины

Vite — современный инструмент сборки для фронтенд-проектов. Dev-сервер на ESM, HMR за миллисекунды, Rollup-бандлер для production, плагины и конфигурация.

vitebundlerhmrrollupbuilddev-server

Что такое Vite

Vite (читается «вит», фр. «быстрый») — это инструмент сборки, созданный Эваном Ю (автором Vue.js). Он решает главную проблему Webpack: медленный старт dev-сервера на больших проектах.

Традиционный подход: при запуске dev-сервера бандлер собирает весь граф зависимостей, транспайлит код и только потом отдаёт браузеру. На проекте с 1000 модулей это занимает 30–60 секунд.

Vite делает иначе:

  1. Dev-сервер — не собирает ничего заранее. Браузер сам загружает модули через ES-импорты (<script type="module">). Зависимости (из node_modules)_pre-бандлятся один раз с помощью esbuild (на Go, очень быстрый).
  2. Production-сборка — использует Rollup для оптимизации, tree-shaking и code splitting.

Результат: dev-сервер стартует за 300 мс независимо от размера проекта.

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

npm create vite@latest my-app        # Интерактивный выбор шаблона
npm create vite@latest my-app -- --template vue-ts   # Сразу с шаблоном
npm create vite@latest my-app -- --template react-ts
npm create vite@latest my-app -- --template vanilla-ts

Доступные шаблоны: vanilla, vue, react, preact, lit, svelte (с суффиксом -ts для TypeScript).

Структура проекта

my-app/
  index.html                ← Точка входа (не src/main.ts!)
  vite.config.ts            ← Конфигурация Vite
  tsconfig.json
  public/                   ← Статика (копируется как есть)
    favicon.ico
  src/
    main.ts                 ← Entry-файл
    App.vue                 ← Корневой компонент
    style.css
    assets/
      logo.svg

Главная фишка: index.html находится в корне, а не в public/. Vite обрабатывает его как модуль — URL в <script> и <link> автоматически резолвятся.

<!-- index.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Мое приложение</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.ts"></script>
</body>
</html>

Конфигурация

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

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
    },
  },
  server: {
    port: 3000,
    open: true,
    cors: true,
  },
  build: {
    outDir: 'dist',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
        },
      },
    },
  },
})

Основные опции

server — настройки dev-сервера

server: {
  port: 3000,                 // Порт
  host: '0.0.0.0',           // Доступен в локальной сети
  open: true,                 // Открыть браузер
  cors: true,                 // CORS для API
  proxy: {                    // Прокси для API
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, ''),
    },
  },
  watch: {
    ignored: ['**/coverage/**'],  // Не перезапускать при изменении этих файлов
  },
}

build — настройки production-сборки

build: {
  outDir: 'dist',              // Папка результата
  sourcemap: true,             // Генерировать source maps
  minify: 'terser',            // 'terser' | 'esbuild'
  chunkSizeWarningLimit: 1000, // Предупреждение о размере чанка (KB)
  rollupOptions: {
    input: {                   // Несколько точек входа (MPA)
      main: resolve(__dirname, 'index.html'),
      admin: resolve(__dirname, 'admin.html'),
    },
    output: {
      entryFileNames: 'assets/[name]-[hash].js',
      chunkFileNames: 'assets/[name]-[hash].js',
      assetFileNames: 'assets/[name]-[hash][extname]',
    },
  },
}

resolve — резолвинг модулей

resolve: {
  alias: {
    '@': resolve(__dirname, 'src'),
    '@components': resolve(__dirname, 'src/components'),
    '#': resolve(__dirname, 'types'),
  },
  extensions: ['.ts', '.tsx', '.js', '.jsx', '.vue'],
}

css — работа со стилями

css: {
  modules: {
    localsConvention: 'camelCase',   // .my-class → styles.myClass
  },
  preprocessorOptions: {
    scss: {
      additionalData: `@use "@/styles/variables" as *;`,
    },
  },
}

HMR (Hot Module Replacement)

HMR — главная фишка Vite. Когда вы меняете файл, Vite отправляет браузеру только изменённый модуль, не перезагружая страницу. Состояние приложения сохраняется.

Для Vue и React HMR работает из коробки. Для vanilla JS:

if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    if (newModule) {
      newModule.render()
    }
  })
}

import.meta.env — переменные окружения

Vite автоматически загружает переменные из .env-файлов:

# .env (все окружения)
VITE_APP_TITLE=Мое приложение

# .env.development
VITE_API_URL=http://localhost:8080

# .env.production
VITE_API_URL=https://api.myapp.com

Только переменные с префиксом VITE_ доступны в браузере:

console.log(import.meta.env.VITE_API_URL)
console.log(import.meta.env.MODE)         // 'development' | 'production'
console.log(import.meta.env.DEV)          // true в dev
console.log(import.meta.env.PROD)         // true в production
console.log(import.meta.env.BASE_URL)     // из base в конфиге

Режимы

vite                                # dev-сервер (mode: development)
vite build                          # production-сборка
vite build --mode staging           # Загрузит .env.staging
vite preview                        # Превью production-сборки

Глобальные импорты (Glob Import)

Vite умеет импортировать сразу несколько файлов по паттерну:

const modules = import.meta.glob('./components/*.vue')

// Результат:
// {
//   './components/Header.vue': () => import('./components/Header.vue'),
//   './components/Footer.vue': () => import('./components/Footer.vue'),
// }

Eager-загрузка (без lazy):

const modules = import.meta.glob('./components/*.vue', { eager: true })

Практический пример — авторегистрация компонентов:

const components = import.meta.glob('./components/*.vue', { eager: true })

Object.entries(components).forEach(([path, module]: [string, any]) => {
  const name = path.match(/\.\/components\/(.*)\.vue$/)?.[1]
  if (name) {
    app.component(name, module.default)
  }
})

Статические ресурсы

import logoUrl from '@/assets/logo.svg'         // URL строки
import logoUrl from '@/assets/logo.svg?url'      // Явно URL
import logoRaw from '@/assets/logo.svg?raw'      // Как строку (SVG-код)

const workers = new Worker(new URL('./worker.ts', import.meta.url))

Изображения, шрифты и SVG, импортированные в JS, будут оптимизированы (хешированное имя, small файлы → inline data URI).

Плагины

Vite использует плагины на основе Rollup (с расширениями).

Популярные плагины

import vue from '@vitejs/plugin-vue'             // Vue SFC
import react from '@vitejs/plugin-react'          // React + Fast Refresh
import svgr from 'vite-plugin-svgr'               // SVG как React-компоненты
import compression from 'vite-plugin-compression'  // Gzip/Brotli
import mkcert from 'vite-plugin-mkcert'           // Локальный HTTPS
import { visualizer } from 'rollup-plugin-visualizer'  // Визуализация бандла
import { VitePWA } from 'vite-plugin-pwa'         // PWA

Пример конфигурации с плагинами

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import compression from 'vite-plugin-compression'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    compression({ algorithm: 'gzip' }),
    visualizer({ open: true, gzipSize: true }),
  ],
})

Свой плагин

const myPlugin = {
  name: 'my-plugin',
  resolveId(id) {
    if (id === 'virtual:my-module') {
      return id
    }
  },
  load(id) {
    if (id === 'virtual:my-module') {
      return 'export default "This is virtual!"'
    }
  },
  transform(code, id) {
    if (id.endsWith('.vue')) {
      return code.replace(/console\.log\(.*\)/g, '')
    }
  },
}

export default defineConfig({
  plugins: [myPlugin],
})

Хуки плагинов: configResolved, configureServer, transformIndexHtml, handleHotUpdate, buildStart, buildEnd, closeBundle.

Прокси для API

При разработке фронтенд обычно запускается на localhost:5173, а бэкенд — на localhost:8080. Прямой запрос с фронтенда вызовет CORS. Прокси решает это:

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
})

Теперь fetch('/api/users')http://localhost:8080/users.

Деплой

SPA-маршрутизация

При деплое SPA нужно, чтобы все пути вели к index.html:

nginx:

location / {
  try_files $uri $uri/ /index.html;
}

Vercel (vercel.json):

{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

Netlify (_redirects):

/* /index.html 200

base — если приложение не в корне

export default defineConfig({
  base: '/my-app/',                 // Все URL будут начинаться с /my-app/
})

Сравнение с Webpack

КритерийViteWebpack
Старт dev-сервера< 1 сек10–60 сек
HMR< 50 мс1–5 сек
КонфигурацияПростой объектСложный, много boilerplate
Production-сборкаRollupWebpack
Код-сплиттингАвтоматическийНужна настройка
Экосистема плагиновРастёт (совместим с Rollup)Огромная
TypeScriptИз коробки (esbuild)Нужен loader

Итог

  • Vite — стандарт де-факто для новых Vue, React, Svelte-проектов
  • Dev-сервер на ESM обеспечивает мгновенный старт и HMR
  • Production-сборка через Rollup даёт оптимальный бандл
  • Конфигурация простая, но при необходимости — гибкая
  • Для Nuxt 3/4 Vite используется по умолчанию
  • Если вы начинаете новый проект — выбирайте Vite, а не Webpack