Конфигурация, переменные окружения и оптимизация

Перед деплоем код надо подготовить: вынести настройки в окружение, разделить публичное и секретное, облегчить бандл.
Суть: nuxt.config.ts — единая точка конфигурации. runtimeConfig разделяет приватные (серверные) и public значения, переменные окружения приходят из .env. Оптимизация — это ленивые компоненты, контроль размера бандла и аккуратные зависимости.

Прежде чем выкатывать приложение в мир, его нужно «причесать»: настройки не должны быть зашиты в код, секреты — попасть в браузер, а бандл — раздуться. Этот урок — про подготовку к продакшену.

Центр конфигурации — nuxt.config.ts. Здесь подключают модули, задают routeRules и определяют runtimeConfig — значения, которые меняются между окружениями (dev/prod):

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: "",                 // приватно: только сервер
    public: {
      apiBase: "/api",             // public: доступно клиенту
    },
  },
})

Ключевое разделение: всё под public попадает и в браузер, всё остальное — только на сервер. Значения подставляются из переменных окружения: переменная NUXT_API_SECRET переопределит apiSecret, а NUXT_PUBLIC_API_BASEpublic.apiBase. Локально их кладут в файл .env (который не коммитят), на проде — в окружение хостинга. Читают конфиг через useRuntimeConfig().

Оптимизация — вторая половина подготовки. Ленивые компоненты (<LazyHeavyChart />) грузятся только когда нужны. Тяжёлые зависимости стоит проверять на размер. Цель — маленький первый бандл и быстрая интерактивность.

   runtimeConfig: что куда попадает

   nuxt.config runtimeConfig
        |
        +-- apiSecret      -> ТОЛЬКО сервер (из NUXT_API_SECRET)
        +-- public.apiBase -> сервер И клиент (из NUXT_PUBLIC_API_BASE)

   .env (локально) / переменные хостинга (прод) -> подстановка

Гибридный рендеринг — это во многом про деньги и масштаб. Каждый SSR-запрос стоит процессорного времени сервера: под нагрузкой это превращается в счёт за инфраструктуру. Перевод статичных и редко меняющихся страниц на prerender или кеш SWR разгружает сервер на порядок, потому что готовый ответ отдаётся без вычислений. SSR при этом остаётся там, где он действительно нужен — на персонализированных и часто обновляемых страницах. Грамотно расставленные routeRules позволяют сайту выдерживать всплески трафика без линейного роста расходов, и именно поэтому крупные проекты так ценят эту гибкость.

Как работает под капотом

На этапе сборки Nuxt подставляет public-значения в клиентский бандл, а приватные оставляет только в серверном. Переменные окружения с префиксом NUXT_ автоматически мапятся на ключи runtimeConfig в момент запуска — поэтому одно и то же приложение работает в разных окружениях без пересборки. Ленивые компоненты сборщик выделяет в отдельные чанки, подгружаемые по требованию, что уменьшает стартовый бандл.

Смоделируем слияние конфигурации с переменными окружения и разделение public/приватного:

// runtimeConfig + env -> что увидит сервер и что клиент.
function resolveConfig(base, env) {
  const cfg = JSON.parse(JSON.stringify(base));   // копия
  if (env.NUXT_API_SECRET) cfg.apiSecret = env.NUXT_API_SECRET;
  if (env.NUXT_PUBLIC_API_BASE) cfg.public.apiBase = env.NUXT_PUBLIC_API_BASE;
  return cfg;
}

const base = { apiSecret: "", public: { apiBase: "/api" } };
const prod = resolveConfig(base, {
  NUXT_API_SECRET: "prod-key-123",
  NUXT_PUBLIC_API_BASE: "https://shop.example/api",
});

console.log("Сервер видит весь конфиг:", prod);
console.log("Клиент видит ТОЛЬКО public:", prod.public);
console.log("Секрет в клиентском бандле? Нет — apiSecret остаётся на сервере.");

Попробуй сам ▶ — окружение переопределяет значения без пересборки, а клиент получает лишь public. Так Nuxt держит секреты в безопасности.

Частые ошибки

  • Класть секрет в public. Всё под public уходит в браузер — там не место ключам.
  • Коммитить .env. Файл с секретами не должен попадать в репозиторий.
  • Игнорировать размер бандла. Тяжёлая зависимость в стартовом чанке замедляет всех; выносите её в lazy.

Best practices

  • Всё, что отличается между окружениями, — в runtimeConfig и переменные окружения, а не в код.
  • Секреты — вне public; в репозитории держите только .env.example без значений.
  • Тяжёлые и не сразу нужные компоненты — ленивыми (Lazy-префикс).

Итог: грамотная конфигурация отделяет код от окружения, прячет секреты и облегчает бандл. Приложение готово к выкату. Финальный урок — собственно деплой.

Проверьте себя
1. Что произойдёт со значением, помещённым в public-секцию runtimeConfig?
AОно останется только на сервере
BОно попадёт и в браузерный бандл, став доступным клиенту
CОно будет зашифровано
DОно удалится при сборке
2. Зачем выносить настройки в runtimeConfig и переменные окружения вместо жёсткого кода?
AЧтобы код красивее выглядел
BЧтобы одно приложение работало в разных окружениях (dev/prod) без пересборки, подставляя значения из окружения
CЭто требуется для работы Vue
DЧтобы отключить SSR