Условная конфигурация по режиму и команде

Конфигурация в Vite может изменяться в зависимости от:

  • текущей команды (serve или build);
  • режима (development, production, staging, test и любых пользовательских режимов);
  • переменных окружения;
  • платформы;
  • внешних параметров запуска.

Такой подход позволяет использовать один файл конфигурации для разных сценариев:

  • локальная разработка;
  • production-сборка;
  • тестовое окружение;
  • CI/CD;
  • SSR;
  • библиотечная сборка;
  • анализ бандла.

В отличие от статических конфигураций, Vite поддерживает программное формирование настроек через функции и динамические условия.


Базовая структура условной конфигурации

Файл vite.config.js или vite.config.ts может экспортировать не объект, а функцию.

Простейший пример:

import { defineConfig } from 'vite'

export default defineConfig(({ command, mode }) => {
    return {
        server: {
            port: command === 'serve' ? 5173 : 4173
        }
    }
})

Vite передаёт в функцию объект контекста, содержащий:

Поле Назначение
command Текущая команда
mode Активный режим
isSsrBuild SSR-сборка
isPreview Режим preview

Параметр command

Значения command

Существует два основных значения:

Значение Команда
serve vite, vite dev
build vite build

Проверка команды

export default defineConfig(({ command }) => {
    if (command === 'serve') {
        return {
            server: {
                open: true
            }
        }
    }

    return {
        build: {
            sourcemap: false
        }
    }
})

Использование разных плагинов

Во время разработки и production-сборки часто требуются разные плагины.

import legacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ command }) => {
    return {
        plugins: [
            vue(),
            command === 'build' && legacy()
        ].filter(Boolean)
    }
})

Здесь:

  • vue() подключается всегда;
  • legacy() только при production-сборке.

Разделение server и build

Некоторые секции конфигурации актуальны только для конкретной команды.

Для serve

export default defineConfig(({ command }) => {
    if (command === 'serve') {
        return {
            server: {
                host: '0.0.0.0',
                port: 3000,
                open: true
            }
        }
    }

    return {}
})

Для build

export default defineConfig(({ command }) => {
    if (command === 'build') {
        return {
            build: {
                minify: 'esbuild',
                sourcemap: true
            }
        }
    }

    return {}
})

Параметр mode

Что такое mode

mode определяет активный режим работы приложения.

По умолчанию:

Команда Режим
vite development
vite build production

Передача пользовательского режима

vite build --mode staging

Тогда:

export default defineConfig(({ mode }) => {
    console.log(mode)
})

Выведет:

staging

Условная настройка по mode

export default defineConfig(({ mode }) => {
    return {
        base: mode === 'production'
            ? '/production/'
            : '/'
    }
})

Разные API-адреса

export default defineConfig(({ mode }) => {
    return {
        define: {
            __API_URL__: JSON.stringify(
                mode === 'production'
                    ? 'https://api.site.com'
                    : 'http://localhost:3001'
            )
        }
    }
})

Пользовательские режимы

Создание staging-режима

vite build --mode staging

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

export default defineConfig(({ mode }) => {
    return {
        build: {
            sourcemap: mode === 'staging'
        }
    }
})

Несколько окружений

export default defineConfig(({ mode }) => {

    const config = {
        development: {
            api: 'http://localhost:3000'
        },

        staging: {
            api: 'https://staging-api.site.com'
        },

        production: {
            api: 'https://api.site.com'
        }
    }

    return {
        define: {
            __API__: JSON.stringify(config[mode].api)
        }
    }
})

loadEnv и переменные окружения

Загрузка env-переменных

Vite предоставляет функцию loadEnv.

import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {

    const env = loadEnv(mode, process.cwd())

    return {
        server: {
            port: Number(env.VITE_PORT)
        }
    }
})

Файлы окружения

Vite автоматически поддерживает:

Файл Назначение
.env Общие переменные
.env.local Локальные переменные
.env.development Development
.env.production Production
.env.staging Пользовательский режим

Пример .env.production

VITE_API_URL=https://api.site.com
VITE_PORT=8080

Пример .env.development

VITE_API_URL=http://localhost:3000
VITE_PORT=5173

Префикс VITE_

Причина существования префикса

В браузерный код попадают только переменные, начинающиеся с VITE_.

Допустим:

VITE_API_URL=https://api.site.com
SECRET_KEY=123456

В клиентском коде:

console.log(import.meta.env.VITE_API_URL)

Работает.

Но:

console.log(import.meta.env.SECRET_KEY)

Не работает.

Это механизм защиты приватных переменных.


Условная настройка alias

Разные alias для режимов

import path from 'path'

export default defineConfig(({ mode }) => {
    return {
        resolve: {
            alias: {
                '@api': mode === 'mock'
                    ? path.resolve(__dirname, 'src/api/mock')
                    : path.resolve(__dirname, 'src/api/real')
            }
        }
    }
})

Условная настройка optimizeDeps

Разные зависимости для development

export default defineConfig(({ command }) => {
    return {
        optimizeDeps: {
            include: command === 'serve'
                ? ['lodash']
                : []
        }
    }
})

Условная настройка build

Разная минификация

export default defineConfig(({ mode }) => {
    return {
        build: {
            minify: mode === 'development'
                ? false
                : 'esbuild'
        }
    }
})

Управление sourcemap

export default defineConfig(({ mode }) => {
    return {
        build: {
            sourcemap:
                mode === 'staging' ||
                mode === 'development'
        }
    }
})

Разные output-директории

export default defineConfig(({ mode }) => {
    return {
        build: {
            outDir:
                mode === 'production'
                    ? 'dist'
                    : 'dist-test'
        }
    }
})

Условная настройка proxy

Разные backend-серверы

export default defineConfig(({ mode }) => {

    const targets = {
        development: 'http://localhost:3000',
        staging: 'https://staging-api.site.com',
        production: 'https://api.site.com'
    }

    return {
        server: {
            proxy: {
                '/api': {
                    target: targets[mode],
                    changeOrigin: true
                }
            }
        }
    }
})

Условная настройка define

Инъекция compile-time констант

export default defineConfig(({ mode }) => {
    return {
        define: {
            __DEV__: mode === 'development',
            __PROD__: mode === 'production'
        }
    }
})

Использование:

if (__DEV__) {
    console.log('debug')
}

Во время сборки Vite заменяет значения статически.


Разделение конфигурации

Вынос частей конфигурации

Крупные проекты обычно разделяют конфигурацию.

Структура:

config/
    vite.base.js
    vite.dev.js
    vite.prod.js

Базовая конфигурация

// vite.base.js

export const baseConfig = {
    resolve: {
        alias: {
            '@': '/src'
        }
    }
}

Development-конфигурация

// vite.dev.js

export const devConfig = {
    server: {
        port: 3000
    }
}

Production-конфигурация

// vite.prod.js

export const prodConfig = {
    build: {
        minify: 'esbuild'
    }
}

Объединение конфигураций

import { defineConfig, mergeConfig } from 'vite'

import { baseConfig } from './config/vite.base'
import { devConfig } from './config/vite.dev'
import { prodConfig } from './config/vite.prod'

export default defineConfig(({ command }) => {

    if (command === 'serve') {
        return mergeConfig(baseConfig, devConfig)
    }

    return mergeConfig(baseConfig, prodConfig)
})

mergeConfig

Назначение mergeConfig

mergeConfig корректно объединяет:

  • plugins;
  • resolve;
  • server;
  • build;
  • css;
  • optimizeDeps.

Пример:

import { mergeConfig } from 'vite'

const a = {
    server: {
        port: 3000
    }
}

const b = {
    build: {
        sourcemap: true
    }
}

const result = mergeConfig(a, b)

Условное подключение анализаторов

Подключение rollup-plugin-visualizer

import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig(({ mode }) => {
    return {
        plugins: [
            mode === 'analyze' &&
                visualizer({
                    open: true
                })
        ].filter(Boolean)
    }
})

Запуск:

vite build --mode analyze

Условная SSR-конфигурация

Использование isSsrBuild

export default defineConfig(({ isSsrBuild }) => {
    return {
        build: {
            minify: !isSsrBuild
        }
    }
})

Разные external-зависимости

export default defineConfig(({ isSsrBuild }) => {
    return {
        ssr: {
            external: isSsrBuild
                ? ['mysql2']
                : []
        }
    }
})

Использование process.env

Работа с системными переменными

export default defineConfig(() => {

    const isDocker =
        process.env.DOCKER === 'true'

    return {
        server: {
            host: isDocker
                ? '0.0.0.0'
                : 'localhost'
        }
    }
})

Комбинирование условий

Проверка mode и command одновременно

export default defineConfig(({ mode, command }) => {

    const isDev =
        mode === 'development'

    const isBuild =
        command === 'build'

    return {
        build: {
            sourcemap:
                isDev && isBuild
        }
    }
})

Формирование флагов

Централизация условий

Большие конфигурации часто используют промежуточные флаги.

export default defineConfig(({ mode, command }) => {

    const isDev =
        mode === 'development'

    const isProd =
        mode === 'production'

    const isServe =
        command === 'serve'

    const isBuild =
        command === 'build'

    return {
        server: {
            open: isServe
        },

        build: {
            minify: isProd
        }
    }
})

Такой подход:

  • улучшает читаемость;
  • упрощает поддержку;
  • уменьшает количество повторений;
  • делает конфигурацию предсказуемой.

Асинхронная конфигурация

Возврат Promise

Vite поддерживает асинхронные конфигурации.

export default defineConfig(async ({ mode }) => {

    const config =
        await loadRemoteConfig(mode)

    return {
        define: {
            __REMOTE_CONFIG__:
                JSON.stringify(config)
        }
    }
})

Типизация в TypeScript

vite.config.ts

import { defineConfig } from 'vite'
import type { UserConfig } from 'vite'

export default defineConfig(
    ({ mode }): UserConfig => {

        return {
            build: {
                sourcemap:
                    mode === 'development'
            }
        }
    }
)

Практическая структура production-конфигурации

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode, command }) => {

    const env = loadEnv(mode, process.cwd())

    const isProd = mode === 'production'
    const isDev = mode === 'development'
    const isBuild = command === 'build'

    return {
        plugins: [
            vue()
        ],

        base: isProd
            ? '/app/'
            : '/',

        server: {
            port: Number(env.VITE_PORT)
        },

        build: {
            sourcemap: isDev,
            minify: isProd,
            outDir: isProd
                ? 'dist'
                : 'dist-dev'
        },

        define: {
            __APP_VERSION__:
                JSON.stringify(process.env.npm_package_version)
        },

        css: {
            devSourcemap: isDev
        }
    }
})