← Курс/Vite: настройка Vue проекта#250 из 257+25 XP

Vite: настройка Vue проекта

Что такое Vite

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

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

npm create vue@latest my-app
cd my-app
npm install
npm run dev

Команда create vue запустит интерактивный мастер, где можно выбрать TypeScript, Vue Router, Pinia, Vitest и другие инструменты.

Конфигурация vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      // @ теперь указывает на папку src/
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
  server: {
    port: 3000,
    open: true,       // открыть браузер при старте
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: 'dist',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
        },
      },
    },
  },
})

Переменные окружения (.env)

Vite поддерживает файлы .env, .env.development, .env.production. Переменные доступны только если начинаются с VITE_:

# .env.development
VITE_API_URL=http://localhost:8080/api
VITE_APP_TITLE=Мой сайт (DEV)

# .env.production
VITE_API_URL=https://api.mysite.com
VITE_APP_TITLE=Мой сайт

В коде:

const apiUrl = import.meta.env.VITE_API_URL
const title = import.meta.env.VITE_APP_TITLE
const isDev = import.meta.env.DEV       // true в development
const isProd = import.meta.env.PROD     // true в production
const mode = import.meta.env.MODE       // 'development' или 'production'

Оптимизация сборки

Команда npm run build запускает Rollup и создаёт оптимизированный бандл в папке dist/:

dist/
├── index.html
└── assets/
    ├── index-Cx3b1Rg2.js    (основной чанк)
    ├── vendor-BkZ3vD9a.js   (vue, vue-router...)
    └── index-DpnGxI8u.css

Для анализа размера бандла используй:

npm install -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    visualizer({ open: true }),  // откроет интерактивную карту после build
  ],
})

Режимы (modes)

npm run dev          # mode: development
npm run build        # mode: production
npx vite --mode staging  # mode: staging (читает .env.staging)

Vite — это не просто замена Webpack. Благодаря HMR (Hot Module Replacement) изменения в коде отображаются в браузере практически мгновенно, без полной перезагрузки страницы.

Примеры

Эмуляция работы с переменными окружения и псевдонимами путей — как Vite обрабатывает import.meta.env и алиасы

// ============================================
// Эмуляция import.meta.env из Vite
// ============================================

// В реальном Vite эти значения инжектируются при сборке.
// Здесь мы воспроизводим логику вручную.

function createEnv(mode, rawEnv) {
  const filtered = {}

  // Vite передаёт в браузер ТОЛЬКО переменные с префиксом VITE_
  for (const [key, value] of Object.entries(rawEnv)) {
    if (key.startsWith('VITE_')) {
      filtered[key] = value
    }
  }

  // Vite также добавляет встроенные переменные
  return {
    ...filtered,
    MODE: mode,
    DEV: mode !== 'production',
    PROD: mode === 'production',
    SSR: false,
    BASE_URL: '/',
  }
}

// Симулируем .env.development
const devRawEnv = {
  VITE_API_URL: 'http://localhost:8080/api',
  VITE_APP_TITLE: 'Мой сайт (DEV)',
  SECRET_KEY: 'this-is-secret',   // НЕ начинается с VITE_ — не попадёт в браузер
  DATABASE_URL: 'postgres://...',  // то же самое
}

// Симулируем .env.production
const prodRawEnv = {
  VITE_API_URL: 'https://api.mysite.com',
  VITE_APP_TITLE: 'Мой сайт',
  SECRET_KEY: 'prod-secret',
  DATABASE_URL: 'postgres://prod...',
}

console.log('=== Development окружение ===')
const devEnv = createEnv('development', devRawEnv)
console.log('import.meta.env:', devEnv)
console.log('API URL:', devEnv.VITE_API_URL)
console.log('Is DEV?', devEnv.DEV)
console.log('SECRET_KEY доступен?', 'SECRET_KEY' in devEnv)  // false — безопасно

console.log('\n=== Production окружение ===')
const prodEnv = createEnv('production', prodRawEnv)
console.log('import.meta.env:', prodEnv)
console.log('API URL:', prodEnv.VITE_API_URL)
console.log('Is PROD?', prodEnv.PROD)

// ============================================
// Эмуляция алиасов путей (@ -> src/)
// ============================================

console.log('\n=== Разрешение алиасов путей ===')

function createAliasResolver(aliases) {
  return function resolvePath(importPath) {
    for (const [alias, target] of Object.entries(aliases)) {
      if (importPath.startsWith(alias)) {
        return importPath.replace(alias, target)
      }
    }
    return importPath  // без изменений
  }
}

const resolve = createAliasResolver({
  '@': '/project/src',
  '@components': '/project/src/components',
  '@utils': '/project/src/utils',
})

const imports = [
  '@/components/Button.vue',
  '@/types/user.ts',
  '@components/Header.vue',
  '@utils/format.ts',
  './relative/path.ts',  // относительный путь — не меняется
]

for (const imp of imports) {
  console.log(`"${imp}" -> "${resolve(imp)}"`)
}

// ============================================
// Эмуляция manualChunks — разбивка бандла
// ============================================

console.log('\n=== Стратегия разбивки бандла ===')

const modules = [
  { name: 'vue', size: 80 },
  { name: 'vue-router', size: 40 },
  { name: 'pinia', size: 30 },
  { name: 'chart.js', size: 200 },
  { name: 'd3', size: 300 },
  { name: 'App.vue', size: 5 },
  { name: 'HomePage.vue', size: 10 },
  { name: 'api.ts', size: 8 },
]

const manualChunks = {
  vendor: ['vue', 'vue-router', 'pinia'],
  charts: ['chart.js', 'd3'],
}

function assignChunks(modules, manualChunks) {
  const chunks = {}

  for (const mod of modules) {
    let assigned = false
    for (const [chunkName, chunkModules] of Object.entries(manualChunks)) {
      if (chunkModules.includes(mod.name)) {
        if (!chunks[chunkName]) chunks[chunkName] = { modules: [], totalSize: 0 }
        chunks[chunkName].modules.push(mod.name)
        chunks[chunkName].totalSize += mod.size
        assigned = true
        break
      }
    }
    if (!assigned) {
      if (!chunks['index']) chunks['index'] = { modules: [], totalSize: 0 }
      chunks['index'].modules.push(mod.name)
      chunks['index'].totalSize += mod.size
    }
  }

  return chunks
}

const result = assignChunks(modules, manualChunks)
for (const [chunk, info] of Object.entries(result)) {
  console.log(`Chunk "${chunk}": ${info.totalSize}KB — [${info.modules.join(', ')}]`)
}