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

Механизм environments в Vite представляет собой слой конфигурации, который позволяет разделять и изолировать разные среды выполнения приложения внутри одного проекта. В отличие от классической модели, где конфигурация часто ограничивается двумя режимами — development и production, подход Vite строится вокруг расширяемых окружений, которые могут включать произвольные цели сборки, runtime-контексты и сценарии исполнения.

Базовая концепция environments

Environments в Vite — это именованные контексты, в рамках которых определяются:

  • параметры сборки
  • переменные окружения
  • стратегия бандлинга
  • целевая платформа исполнения
  • настройки SSR и client-side кода

Каждое окружение можно рассматривать как независимую конфигурационную единицу, которая наследует базовые настройки проекта, но при этом может их переопределять.

Основная идея заключается в том, чтобы уйти от жесткой дихотомии dev/prod и перейти к модели:

base environment → derived environments → runtime-specific overrides

Архитектура environments в Vite

Внутренне Vite рассматривает environment как объект конфигурации, который создается на этапе инициализации сервера или сборщика.

Ключевые компоненты:

  • mode — строковый идентификатор режима
  • commandserve или build
  • isSsrBuild — флаг SSR-сборки
  • configEnv — объект, содержащий переменные окружения
  • root — корневая директория проекта

В расширенной модели environments добавляются дополнительные сущности:

  • envName — имя среды (например, client, server, edge)
  • define — инлайнинг глобальных переменных
  • resolve — правила резолвинга модулей
  • build — параметры сборки под конкретную среду

Функция defineConfig и environments

Конфигурация Vite обычно экспортируется через defineConfig, но в контексте environments она может возвращать динамическую структуру, зависящую от текущего окружения.

import { defineConfig } from 'vite'

export default defineConfig(({ command, mode, isSsrBuild }) => {
  return {
    build: {
      sourcemap: mode === 'development'
    }
  }
})

В расширенном подходе environments логика становится более детализированной:

export default defineConfig(({ command, mode }) => {
  const isClient = mode === 'client'
  const isServer = mode === 'server'

  return {
    build: {
      target: isClient ? 'es2020' : 'node18'
    }
  }
})

Создание кастомных environments

Vite позволяет формировать кастомные среды через расширение конфигурации и разделение точек входа.

Типичная структура:

vite.config.js
env/
  client.env.js
  server.env.js
  edge.env.js

Каждый файл описывает свою часть конфигурации:

// env/client.env.js
export default {
  build: {
    outDir: 'dist/client',
    target: 'esnext'
  }
}
// env/server.env.js
export default {
  build: {
    outDir: 'dist/server',
    target: 'node18'
  }
}

Далее происходит композиция:

import clientEnv from './env/client.env'
import serverEnv from './env/server.env'

export default defineConfig(({ mode }) => {
  if (mode === 'client') {
    return clientEnv
  }

  if (mode === 'server') {
    return serverEnv
  }

  return {}
})

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

Vite использует систему import.meta.env, которая тесно связана с текущим environment.

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

  • переменные подгружаются из .env, .env.[mode]
  • доступны через loadEnv
  • инлайнятся на этапе сборки
import { loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')

  return {
    define: {
      __API_URL__: JSON.stringify(env.API_URL)
    }
  }
})

В рамках environments важно разделять:

  • build-time env
  • runtime env (SSR или edge execution)
  • client-exposed env (VITE_ prefix)

SSR environments

Одним из ключевых сценариев использования environments является разделение клиентской и серверной сборки.

SSR в Vite подразумевает два независимых графа модулей:

  • client bundle
  • server bundle

Конфигурация environments позволяет явно разделить их:

export default defineConfig(({ ssrBuild }) => {
  return {
    build: {
      ssr: ssrBuild ? true : false,
      rollupOptions: {
        input: ssrBuild ? 'src/entry-server.js' : 'src/entry-client.js'
      }
    }
  }
})

SSR environment обычно отличается:

  • отсутствием DOM API
  • использованием Node.js target
  • внешними зависимостями через ssr.external

Edge environments

Современные архитектуры часто включают edge runtime (Cloudflare Workers, Vercel Edge, Deno Deploy). Vite environments позволяют выделять такие контексты отдельно.

export default defineConfig(({ mode }) => {
  if (mode === 'edge') {
    return {
      build: {
        target: 'es2022',
        minify: true
      },
      ssr: {
        target: 'webworker'
      }
    }
  }
})

Особенности edge environments:

  • отсутствие Node.js API
  • строгие лимиты на размер бандла
  • необходимость полной ESM-совместимости

Разрешение зависимостей в разных environments

Одним из ключевых аспектов является управление ssr.external и ssr.noExternal, которые становятся критичными при разделении environments.

export default defineConfig({
  ssr: {
    external: ['express', 'fastify'],
    noExternal: ['my-shared-lib']
  }
})

В multi-environment подходе это может различаться:

  • server environment — external Node dependencies
  • client environment — полная бандлизация
  • edge environment — минимальный набор совместимых модулей

Изоляция build pipelines

Environments позволяют логически разделять pipeline сборки:

  • трансформация исходников
  • оптимизация зависимостей
  • код-сплиттинг
  • генерация артефактов

Пример различий:

// client
build: {
  cssCodeSplit: true,
  sourcemap: true
}

// server
build: {
  cssCodeSplit: false,
  sourcemap: false
}

Плагины и environments

Vite плагины могут адаптироваться под текущую среду через контекст конфигурации.

export function myPlugin() {
  return {
    name: 'my-plugin',
    config(config, { command, mode }) {
      if (mode === 'server') {
        config.resolve.alias = {
          ...config.resolve.alias,
          '@runtime': '/src/runtime/server'
        }
      }
    }
  }
}

Плагин становится частью environment-aware системы, где поведение изменяется в зависимости от цели сборки.

Гибридные environments

В сложных приложениях встречается гибридная модель:

  • client SPA
  • SSR server
  • edge middleware
  • worker background tasks

Каждое окружение имеет:

  • отдельный entry point
  • собственный build target
  • изолированные зависимости
  • специфичные переменные окружения

Пример конфигурации:

export default defineConfig(({ mode }) => {
  const configs = {
    client: {
      build: { outDir: 'dist/client' }
    },
    server: {
      build: { outDir: 'dist/server', ssr: true }
    },
    worker: {
      build: { outDir: 'dist/worker', target: 'es2022' }
    }
  }

  return configs[mode] || {}
})

Влияние environments на оптимизацию

Разделение environments напрямую влияет на:

  • tree-shaking
  • pre-bundling через esbuild
  • chunking стратегию Rollup
  • кеширование зависимостей

Разные environments позволяют уменьшить пересечение зависимостей и избежать лишнего кода в итоговом бандле.

Особенно это заметно при:

  • SSR + client hydration
  • edge + serverless архитектура
  • monorepo с несколькими runtime

Ошибки конфигурации environments

Частые проблемы при работе с environments:

  • смешивание client и server зависимостей
  • отсутствие разделения entry points
  • неправильный ssr.noExternal
  • утечка Node.js API в client bundle
  • дублирование env переменных

Типичный анти-паттерн:

export default defineConfig({
  build: {
    ssr: true,
    target: 'esnext'
  }
})

Без разделения environments это приводит к некорректному бандлу и runtime-ошибкам.

Структурный подход к environments

Наиболее устойчивый подход включает:

  • явное именование environments
  • отдельные конфигурационные файлы
  • композицию через функции
  • изоляцию build outputs
  • контроль зависимостей через ssr.external

Такая модель делает Vite-конфигурацию предсказуемой и масштабируемой при росте архитектуры приложения.