Миграция Webpack → Vite: типовые отличия (process.env, алиасы, define)
Практические пары «было (Webpack) / стало (Vite)»: process.env.NODE_ENV → import.meta.env, алиасы, DefinePlugin → define. Только документированный API, минимальный показательный код и выводы по миграции.
Как использовать
- Использовать как чеклист при переносе проекта с Webpack на Vite
- Заменить в коде и конфиге по блокам ниже; проверить по списку «что проверить в первую очередь»
Ниже — только реальные, документированные отличия (Vite Env Variables, Vite Config, Webpack DefinePlugin).
1) process.env.NODE_ENV → import.meta.env
Webpack (было)
// В коде приложения
if (process.env.NODE_ENV === 'production') {
// только в проде
}
const apiBase = process.env.REACT_APP_API_URL ?? '';
Почему в Webpack это работало: Webpack при сборке подставляет строки через DefinePlugin (или через mode: 'production'). В бандле нет реального process.env — только подставленные значения. Переменные из .env часто подхватываются через dotenv + DefinePlugin или через CRA/другие шаблоны.
Vite (стало)
// В коде приложения
if (import.meta.env.PROD) {
// только в проде
}
// Кастомные переменные — только с префиксом VITE_, иначе не попадут в клиентский бандл
const apiBase = import.meta.env.VITE_API_URL ?? '';
Почему в Vite иначе: Vite не подменяет process.env в браузерном коде. Используется стандартный import.meta.env: в нём есть MODE, DEV, PROD, SSR, а переменные из .env доступны только если имя начинается с VITE_ (защита от случайной утечки серверных секретов в клиент).
2) Алиасы путей
Webpack (было)
// webpack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
},
},
};
// В коде: import from '@components/Button'
import { Button } from '@components/Button';
Почему в Webpack это работало: В Node-конфиге есть __dirname; Webpack резолвит алиасы на этапе сборки и подставляет реальные пути в граф модулей.
Vite (стало)
// vite.config.ts
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
},
},
});
// В коде — без изменений: import from '@components/Button'
import { Button } from '@components/Button';
Почему в Vite иначе: Конфиг Vite — ESM-модуль, в нём нет __dirname. Нужно получить путь к директории конфига через path.dirname(fileURLToPath(import.meta.url)). Синтаксис алиасов в коде тот же.
3) Глобальные константы (DefinePlugin → define)
Webpack (было)
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
__APP_VERSION__: JSON.stringify(process.env.npm_package_version || '0.0.0'),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
}),
],
};
// В коде
console.log(__APP_VERSION__, __BUILD_TIME__);
Почему в Webpack это работало: DefinePlugin заменяет идентификаторы на строки (или выражения) на этапе сборки. В бандле не остаётся ссылок на __APP_VERSION__ — только подставленное значение.
Vite (стало)
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version || '0.0.0'),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
},
});
// В коде — без изменений
console.log(__APP_VERSION__, __BUILD_TIME__);
Почему в Vite иначе: В Vite нет DefinePlugin; ту же роль играет опция define в корне конфига. Значения тоже передаются как строки (через JSON.stringify), иначе подставится идентификатор, а не строка. process.env.NODE_ENV в Vite не задаётся через define — для режима сборки используются import.meta.env.PROD / import.meta.env.DEV.
Вывод: что чаще всего ломается при миграции
process.envв клиентском коде — код падает с «process is not defined» или переменные пустые. Заменить наimport.meta.envи префиксVITE_для своих переменных из.env.- Алиасы в конфиге — в Vite конфиге нет
__dirname; безfileURLToPath(import.meta.url)путь кsrcзадать нельзя. Импорты в коде менять не нужно. - Глобальные константы — везде, где использовались DefinePlugin/ProvidePlugin, перенести в
defineвvite.configи оставить в коде те же имена. - SVG / raw / file-loader — в Vite нет лоадеров; SVG как компонент или raw-строка делаются через плагины (например
vite-plugin-svgr) или через?raw/?urlсуффиксы. - Публичный путь и base — если приложение не в корне домена, в Vite задать
base: '/app/'; в Webpack часто былоoutput.publicPath.
Что проверить в первую очередь
- Поиск по проекту:
process.env→ заменить наimport.meta.env(и переименовать переменные в.envвVITE_*где нужно). - Конфиг: алиасы через
__dirnameизfileURLToPath(import.meta.url); глобалы из DefinePlugin — вdefine. - Типы (TypeScript): добавить в
env.d.tsобъявлениеinterface ImportMetaEnv { VITE_*: string }и при необходимостиdefine-глобалов, чтобы не было ошибок типов. - Сборка: запустить
vite build, проверить отсутствие «process is not defined» и корректность подставленных констант в бандле. - Dev: запустить
vite, убедиться, что алиасы и переменные окружения работают так же, как в проде.
После этого по необходимости разбирать лоадеры (SVG, CSS-модули, и т.д.) и при необходимости — base и publicPath.