lenec ru

← все посты

Vite 6 environment API: гайд

13K

Vite 6 принёс Environment API — новый способ описывать сборку для нескольких сред (браузер, Node, Worker, edge). Раньше это решалось монолитной конфигурацией с условиями, что приводило к запутанным vite.config.ts на больших проектах. Теперь у каждой среды собственный конфиг, и сборщик аккуратно обслуживает их параллельно.

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

Что такое environment

В Vite 6 каждая «среда» (environment) — это отдельная сборка с собственными настройками: формат модуля, target-окружение, набор плагинов. Стандартные имена: client, ssr, worker. Можно создавать свои.

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  environments: {
    client: {
      build: {
        outDir: 'dist/client',
      },
    },
    ssr: {
      build: {
        outDir: 'dist/server',
        ssr: 'src/entry-server.ts',
      },
    },
    edge: {
      build: {
        outDir: 'dist/edge',
        rollupOptions: { input: 'src/entry-edge.ts' },
      },
      resolve: {
        conditions: ['edge'],
      },
    },
  },
});

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

Зачем это нужно

До Vite 6 типичный SSR-проект выглядел так:

// до 6.x
export default defineConfig({
  build: {
    rollupOptions: {
      output: { entryFileNames: 'client.js' },
    },
  },
  ssr: {
    noExternal: ['react'],
  },
  // ... условия для разных режимов через if (process.env.SSR) ...
});

На больших проектах конфиг разрастался до 200+ строк. С environments всё это разделено по логическим зонам.

Миграция

Если у тебя простой клиентский проект — миграция не нужна. Старый конфиг работает, environments активируются автоматически с дефолтным client.

Если у тебя SSR — стоит переехать на новую модель. Что нужно сделать:

  1. Перенеси содержимое build в environments.client.build.
  2. Содержимое ssr — в environments.ssr.build.
  3. Параметры, специфичные для SSR (noExternal, external) — в environments.ssr.resolve.
  4. Запусти vite build --environment ssr и убедись, что артефакт собирается, как раньше.

Скрипты обычно становятся такими:

{
  "scripts": {
    "build": "vite build && vite build --environment ssr",
    "build:edge": "vite build --environment edge"
  }
}

Условные импорты

Environment API даёт возможность точно контролировать, какие модули попадают в какую сборку. Условие в resolve.conditions:

environments: {
  client: {
    resolve: { conditions: ['browser'] },
  },
  ssr: {
    resolve: { conditions: ['node'] },
  },
}

Когда модуль в package.json имеет exports с разными условиями, Vite выберет правильное:

{
  "exports": {
    ".": {
      "browser": "./dist/browser.js",
      "node": "./dist/node.js",
      "default": "./dist/index.js"
    }
  }
}

До этого подобные условия требовали внешних плагинов или ручных подмен. Сейчас — нативно.

Плагины и среды

Плагины могут указывать, в каких средах они активны:

import type { Plugin } from 'vite';

function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    applyToEnvironment(env) {
      return env.name === 'client';
    },
    transform(code, id) {
      // ...
    },
  };
}

Это особенно полезно для плагинов, которые делают разные вещи на сервере и клиенте, или для плагинов, которые в SSR должны быть отключены (например, тех, что инжектят CSS).

Кастомные среды

Самый интересный сценарий — собственные среды. Например, если ты собираешь два разных клиента (для основного приложения и для встраиваемого виджета):

environments: {
  client: {
    build: { rollupOptions: { input: 'src/main.tsx' } },
  },
  widget: {
    build: {
      outDir: 'dist/widget',
      rollupOptions: {
        input: 'src/widget.tsx',
        output: { format: 'iife', name: 'MyWidget' },
      },
    },
  },
}

И в package.json:

{
  "scripts": {
    "build:all": "vite build && vite build --environment widget"
  }
}

На предыдущей мажорной версии для этого приходилось держать второй конфиг или хитро управлять process.env. С environments — это два блока в одном конфиге, и команда сразу понимает структуру сборки.

Dev-сервер с несколькими средами

В dev-режиме Vite поддерживает все среды одновременно. Это особенно удобно для SSR-проектов: один процесс держит и dev-сервер для клиента, и SSR-рендер. Нет нужды в отдельных портах или сложных перезапусках.

Если используешь Astro или SvelteKit — они уже под капотом интегрированы с Environment API, и тебе ничего делать не надо. Если самостоятельный SSR — добавь обработчик:

import express from 'express';
import { createServer } from 'vite';

const app = express();
const vite = await createServer({ server: { middlewareMode: true } });
app.use(vite.middlewares);

app.use(async (req, res) => {
  const url = req.originalUrl;
  const template = await vite.transformIndexHtml(url, baseHtml);
  const { render } = await vite.ssrLoadModule('/src/entry-server.ts');
  const appHtml = await render(url);
  res.send(template.replace('<!--app-->', appHtml));
});

app.listen(3000);

В 6.x этот код почти не отличается от 5.x, но под капотом Vite использует ту же модель сред — это даёт более предсказуемый dev-server.

Edge runtime

Если деплоишь в Cloudflare Workers, Vercel Edge или подобные — environment API сильно упрощает сборку. Заводишь среду edge с conditions: ['edge', 'worker', 'browser'], и она получает только подходящие версии модулей. Вес итогового бандла часто падает в полтора-два раза по сравнению с обычной SSR-сборкой.

Грабли при миграции

  • Плагины из старых версий могут не поддерживать applyToEnvironment. Большинство мейнстримных уже обновлены, мелкие — могут давать предупреждения. На моём проекте с миграции один такой плагин пришлось заменить на форк.
  • Условия в resolve.conditions жёстче приводятся к версии. Если ты использовал старые костыли вроде require.context, они не сработают.
  • Параллельные сборки требуют больше памяти. На больших проектах с тремя средами и множеством файлов RAM может не хватить — увеличь --max-old-space-size.

В сухом остатке

Environment API — это структурное улучшение, которое в первую очередь нужно SSR и сложным проектам с несколькими целевыми платформами. Для простого SPA — можешь даже не заметить. Если у тебя SSR-проект, edge-функции или несколько клиентов — миграция окупается за день и убирает тонну условий из конфигов.

Для меня основная польза в том, что конфиг стал читаться как описание архитектуры. «Что мы собираем — клиент, SSR, edge?» — глянул в environments, увидел три блока. Это правильная декомпозиция, которую раньше приходилось городить руками.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.