Настройка base для поддиректорий

При развертывании Vite-приложения в поддиректории сайта ключевую роль играет параметр base в конфигурации сборщика. Этот параметр определяет базовый публичный путь, с которого будут загружаться все ресурсы приложения: JavaScript-бандлы, стили, изображения и динамические чанки.


В файле vite.config.js параметр base задаётся на уровне экспорта конфигурации:

import { defineConfig } from 'vite';

export default defineConfig({
  base: '/app/',
});

Значение /app/ означает, что приложение будет развёрнуто не в корне домена, а по адресу:

https://example.com/app/

Без корректного указания base все ассеты будут ссылаться на корень домена, что приводит к 404 при загрузке скриптов и стилей в поддиректории.


Как работает base внутри сборки

Во время сборки Vite подставляет значение base в следующие элементы:

  • пути к JS-чанкам (<script src>)
  • ссылки на CSS-файлы
  • динамически загружаемые модули через import()
  • ассеты из папки public
  • URL, генерируемые через import.meta.env.BASE_URL

Пример:

const url = import.meta.env.BASE_URL + 'images/logo.png';

Если base = '/app/', итоговый URL будет:

/app/images/logo.png

Отличие поведения в dev и build режиме

В режиме разработки (vite dev) параметр base практически не влияет на маршрутизацию сервера разработки. Dev-сервер всегда обслуживает приложение из корня (/), чтобы упростить HMR и работу модулей.

Однако в production-сборке (vite build) base становится критически важным, так как все пути фиксируются на этапе бандлинга.


Использование относительного base

Vite поддерживает относительное значение base:

export default defineConfig({
  base: './',
});

Такой режим используется, когда приложение должно работать:

  • из любого подкаталога
  • при открытии через file://
  • в статических сборках без сервера

В этом случае пути к ресурсам становятся относительными:

./assets/index-xxxx.js

Однако относительный base может ломать некоторые сценарии роутинга и загрузки чанков при сложной структуре приложения.


Поддиректории и маршрутизация SPA

При использовании клиентского роутинга (Vue Router, React Router) необходимо синхронизировать base Vite и базовый путь роутера.

Vue Router

import { createRouter, createWebHistory } from 'vue-router';

export const router = createRouter({
  history: createWebHistory('/app/'),
  routes: [],
});

Здесь важно, чтобы путь совпадал с vite.config.js:

base: '/app/'

Несоответствие приводит к:

  • неправильной обработке deep links
  • 404 при обновлении страницы
  • некорректной генерации ссылок

React Router

import { BrowserRouter } from 'react-router-dom';

<BrowserRouter basename="/app/">
  <App />
</BrowserRouter>

Работа с import.meta.env.BASE_URL

Vite автоматически прокидывает значение base в переменную окружения:

console.log(import.meta.env.BASE_URL);

Это позволяет строить корректные пути к ресурсам без хардкода:

function getAsset(path) {
  return import.meta.env.BASE_URL + path;
}

Пример использования:

const avatar = getAsset('images/avatar.png');

Папка public и base

Файлы из public копируются в корень сборки без обработки. Их путь зависит от base.

Структура:

public/
  logo.png

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

<img src="/logo.png" />

При base = '/app/' Vite преобразует это в:

/app/logo.png

Ошибка возникает, если путь прописан как абсолютный без учёта base:

<img src="/logo.png" />

в контексте неправильного деплоя может привести к загрузке из корня домена.


Динамический base через окружение

Часто значение base зависит от окружения:

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

Это позволяет:

  • использовать корень в dev
  • использовать поддиректорию в production

Развёртывание на GitHub Pages

Для GitHub Pages типичный сценарий:

export default defineConfig({
  base: '/repository-name/',
});

Так как GitHub Pages обслуживает проект из поддиректории:

https://username.github.io/repository-name/

Без правильного base приложение будет пытаться загрузить ресурсы из:

https://username.github.io/assets/...

вместо:

https://username.github.io/repository-name/assets/...

CDN и нестандартные base пути

При использовании CDN base может указывать на внешний домен:

export default defineConfig({
  base: 'https://cdn.example.com/app/',
});

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


Частые ошибки при настройке base

Несовпадение base и router

Если:

base: '/app/'

но:

createWebHistory('/')

возникает рассинхронизация маршрутов и ассетов.


Жёстко прописанные абсолютные пути

<img src="/images/logo.png" />

Такие пути игнорируют base при неправильной настройке и ломаются в поддиректориях.


Отсутствие слэша в конце

Неверно:

base: '/app'

Правильно:

base: '/app/'

Отсутствие завершающего слэша может приводить к некорректной генерации относительных URL.


Взаимодействие base и code splitting

При динамическом импорте:

const module = await import('./module.js');

Vite формирует URL чанка с учётом base. Если base задан неверно, браузер не сможет загрузить файл чанка, даже если основной бандл работает корректно.


Рекомендации по проектированию структуры

  • всегда фиксировать base на уровне инфраструктуры деплоя
  • синхронизировать его с router base
  • избегать абсолютных путей без учета import.meta.env.BASE_URL
  • учитывать CDN как отдельный сценарий base
  • проверять production-сборку отдельно от dev-сервера

Проверка корректности base в сборке

После vite build необходимо анализировать:

  • пути в index.html
  • ссылки на чанки в dist/assets
  • корректность загрузки при обновлении страницы
  • поведение при прямом переходе по URL вложенного маршрута

Особое внимание требуется SPA-приложениям, где неправильный base проявляется только при перезагрузке страницы или прямом заходе по ссылке.