Vite (по-французски «быстро») — это инструмент сборки frontend следующего поколения, который радикально улучшает опыт разработки. Используя нативные ES modules и esbuild, Vite обеспечивает мгновенный запуск сервера и молниеносную Hot Module Replacement (HMR). В этом руководстве рассматривается использование Vite в production-проектах с точки зрения senior-разработчика.
Почему Vite
Vite предлагает преобразующие преимущества:
- Мгновенный запуск сервера: без bundling во время разработки
- Молниеносный HMR: обновления за миллисекунды, а не секунды
- Оптимизированные сборки: production-сборки на базе Rollup
- Независимость от framework: Vue, React, Svelte, vanilla JS
- Современные стандарты: нативный ESM, TypeScript «из коробки»
Начало работы
Создание нового проекта
# Интерактивное создание проекта
npm create vite@latest
# Или укажите параметры
npm create vite@latest my-app -- --template vue
npm create vite@latest my-app -- --template react-ts
npm create vite@latest my-app -- --template vanilla
# Доступные шаблоны
# vanilla, vanilla-ts, vue, vue-ts, react, react-ts,
# react-swc, react-swc-ts, preact, preact-ts,
# lit, lit-ts, svelte, svelte-ts, solid, solid-ts, qwik, qwik-ts
Структура проекта
my-app/
├── public/ # Статические assets (копируются как есть)
│ └── favicon.ico
├── src/
│ ├── assets/ # Обрабатываемые assets
│ │ └── logo.svg
│ ├── components/
│ ├── App.vue # или App.tsx
│ ├── main.js # Точка входа
│ └── style.css
├── index.html # Входной HTML
├── package.json
└── vite.config.js # Конфигурация Vite
Базовые команды
# Запуск dev-сервера
npm run dev
# Сборка для production
npm run build
# Предпросмотр production-сборки
npm run preview
Конфигурация
vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
// Параметры dev-сервера
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
},
// Параметры сборки
build: {
outDir: 'dist',
sourcemap: true,
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia']
}
}
}
},
// Алиасы путей
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils')
}
},
// Параметры CSS
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
},
// Префикс переменных окружения
envPrefix: 'APP_'
});
Переменные окружения
Создайте файлы .env:
# .env
VITE_API_URL=http://localhost:8080
VITE_APP_TITLE=My App
# .env.production
VITE_API_URL=https://api.production.com
# .env.development
VITE_API_URL=http://localhost:3001
Доступ в коде:
// В JavaScript/TypeScript
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE; // 'development' или 'production'
const isDev = import.meta.env.DEV; // boolean
const isProd = import.meta.env.PROD; // boolean
// Определения типов (env.d.ts)
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Работа с assets
Импорт assets
// Импорт как URL
import imgUrl from './image.png';
// imgUrl = '/assets/image.abc123.png' в production
// Импорт как строка (raw)
import shaderCode from './shader.glsl?raw';
// Импорт как worker
import Worker from './worker.js?worker';
const worker = new Worker();
// Динамические импорты
const modules = import.meta.glob('./modules/*.js');
// { './modules/a.js': () => import('./modules/a.js'), ... }
// Жадная загрузка
const modules = import.meta.glob('./modules/*.js', { eager: true });
Директория public
Файлы в public/ отдаются из корня и не обрабатываются:
<!--Прямой доступ-->
<img src="/logo.png" />
<link rel="icon" href="/favicon.ico" />
CSS
// Импорт CSS
import './style.css';
// CSS Modules
import styles from './Component.module.css';
// <div className={styles.container}>
// Препроцессоры (установите sass, less или stylus)
import './style.scss';
import './style.less';
Поддержка TypeScript
Vite поддерживает TypeScript «из коробки»:
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
Примеры для framework
Vue 3
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()]
});
<!-- src/App.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue';
const count = ref(0);
</script>
<template>
<HelloWorld msg="Vite + Vue" />
<button @click="count++">Count: {{ count }}</button>
</template>
<style scoped>
button {
padding: 0.5rem 1rem;
}
</style>
React
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()]
});
// src/App.tsx
import { useState } from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<h1>Vite + React</h1>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
</div>
);
}
export default App;
Server-Side Rendering (SSR)
Базовая настройка SSR
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
build: {
ssr: true
}
});
// server.js
import express from 'express';
import { createServer as createViteServer } from 'vite';
async function createServer() {
const app = express();
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom'
});
app.use(vite.middlewares);
app.use('*', async (req, res) => {
const url = req.originalUrl;
try {
let template = await vite.transformIndexHtml(url,
fs.readFileSync('index.html', 'utf-8')
);
const { render } = await vite.ssrLoadModule('/src/entry-server.js');
const appHtml = await render(url);
const html = template.replace('<!--ssr-outlet-->', appHtml);
res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
} catch (e) {
vite.ssrFixStacktrace(e);
console.error(e);
res.status(500).end(e.message);
}
});
app.listen(3000);
}
createServer();
Импорты только для клиента
Для библиотек, которые не работают в SSR:
// Динамический импорт только для клиента
const Modal = !import.meta.env.SSR
? (await import('bootstrap/js/dist/modal')).default
: null;
// Или используйте опцию ssr в Vite
if (!import.meta.env.SSR) {
import('client-only-library').then(lib => {
// Используйте библиотеку
});
}
Интеграция с Docker
Dockerfile
# Этап сборки
FROM node:20-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production-этап
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Разработка с Docker
# docker-compose.yml
services:
frontend:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- VITE_API_URL=http://api:8080
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev", "--", "--host"]
SSL в разработке
// vite.config.js
import { defineConfig } from 'vite';
import fs from 'fs';
export default defineConfig({
server: {
https: {
key: fs.readFileSync('./certs/localhost.key'),
cert: fs.readFileSync('./certs/localhost.crt')
},
host: true,
port: 3000
}
});
Оптимизация производительности
Code splitting
// Ленивая загрузка маршрутов (Vue Router)
const routes = [
{ path: '/', component: () => import('./views/Home.vue') },
{ path: '/about', component: () => import('./views/About.vue') }
];
// React lazy
const About = React.lazy(() => import('./views/About'));
Анализ сборки
# Установите rollup-plugin-visualizer
npm install -D rollup-plugin-visualizer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
open: true,
gzipSize: true
})
]
});
Конфигурация chunk
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('@vue')) {
return 'vue-vendor';
}
if (id.includes('lodash')) {
return 'lodash';
}
return 'vendor';
}
}
}
}
}
});
Распространённые плагины
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import legacy from '@vitejs/plugin-legacy';
import compression from 'vite-plugin-compression';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
vue(),
// Поддержка legacy-браузеров
legacy({
targets: ['defaults', 'not IE 11']
}),
// Сжатие Gzip
compression({
algorithm: 'gzip',
ext: '.gz'
}),
// Поддержка PWA
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff'
}
})
]
});
Устранение неполадок
404 для маршрутов в production
Настройте сервер для SPA routing:
# nginx.conf
location / {
try_files $uri $uri/ /index.html;
}
HMR не работает
// vite.config.js
export default defineConfig({
server: {
watch: {
usePolling: true // Для Docker/WSL
},
hmr: {
host: 'localhost' // Или ваш dev-хост
}
}
});
Ключевые выводы
- Нативный ESM: разработка использует нативную систему модулей браузера
- esbuild для скорости: предварительный bundling зависимостей выполняется мгновенно
- Rollup для production: оптимизированные, tree-shaken bundles
- Плагины для framework: первоклассная поддержка основных framework
- Переменные окружения: используйте префикс
VITE_ для доступа на клиенте - Готовность к SSR: встроенная поддержка server-side rendering
Vite преобразует frontend-разработку благодаря скорости и простоте — однажды попробовав мгновенный HMR, вы уже не захотите возвращаться назад.