Тёплый старт и кеширование зависимостей

Тёплый старт (warm start) — состояние, при котором сервер разработки Vite запускается повторно с уже подготовленными данными предыдущего запуска. В отличие от холодного старта, где система заново анализирует зависимости, строит граф модулей и выполняет предварительную оптимизацию, тёплый старт использует кешированные результаты.

Основная цель механизма — минимизация времени запуска dev-сервера и ускорение перехода между перезапусками проекта.


Холодный и тёплый старт

Холодный старт

Холодный старт возникает при следующих условиях:

  • первый запуск проекта;
  • удаление кеша Vite;
  • изменение конфигурации зависимостей;
  • обновление пакетов;
  • очистка node_modules/.vite;
  • смена lock-файла (package-lock.json, pnpm-lock.yaml, yarn.lock).

Во время холодного старта Vite:

  1. Анализирует зависимости проекта.
  2. Определяет CommonJS и ESM-модули.
  3. Выполняет pre-bundling через esbuild.
  4. Создаёт кеш оптимизированных модулей.
  5. Формирует внутренний граф зависимостей.

Такой запуск может занимать заметное время в крупных проектах.


Тёплый старт

Тёплый старт использует уже существующие результаты предыдущей оптимизации.

При повторном запуске:

  • кешированные зависимости загружаются сразу;
  • повторный pre-bundling не выполняется;
  • dev-сервер стартует значительно быстрее;
  • уменьшается нагрузка на CPU и файловую систему.

В большинстве случаев Vite автоматически определяет возможность использования тёплого старта.


Кеширование как основа тёплого старта

Главным элементом warm start является кеш зависимостей.

По умолчанию Vite хранит оптимизированные модули в каталоге:

node_modules/.vite

Внутри располагаются:

node_modules/.vite/
├── deps/
├── metadata.json
├── _metadata.json
└── optimized/

Роль pre-bundling

Во время первого запуска Vite выполняет предварительную сборку зависимостей.

Например:

import lodash from 'lodash'
import React from 'react'

Многие npm-пакеты поставляются:

  • в формате CommonJS;
  • с большим количеством внутренних файлов;
  • с медленным разрешением импортов.

Vite объединяет их в оптимизированные ESM-модули при помощи esbuild.

После этого:

  • браузер делает меньше HTTP-запросов;
  • dev-server работает быстрее;
  • повторные старты используют уже готовые результаты.

Как Vite определяет возможность тёплого старта

Vite анализирует несколько факторов:

Содержимое lock-файлов

Проверяются:

package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lockb

Изменение lock-файла считается признаком изменения зависимостей.


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

Изменения в:

optimizeDeps
resolve.alias
plugins
build
server

могут привести к инвалидированию кеша.


Версии пакетов

Обновление зависимостей вызывает повторную оптимизацию.

Например:

npm update react

После этого Vite пересоздаст кеш.


Системные параметры

Некоторые изменения окружения также влияют на warm start:

  • смена версии Node.js;
  • изменение путей проекта;
  • перенос проекта между ОС;
  • изменение режима сборки.

Metadata-файлы

Vite хранит специальную информацию о предыдущем запуске.

Пример:

{
  "hash": "e5a34b9",
  "browserHash": "7f99122",
  "optimized": {
    "react": {
      "src": "../. ./react/index.js",
      "file": "react.js"
    }
  }
}

Metadata используется для:

  • проверки актуальности кеша;
  • сопоставления зависимостей;
  • быстрого восстановления состояния оптимизации.

optimizeDeps и warm start

Раздел optimizeDeps напрямую влияет на работу тёплого старта.


optimizeDeps.include

Принудительная оптимизация зависимостей:

export default defineConfig({
  optimizeDeps: {
    include: ['lodash-es']
  }
})

Если пакет заранее включён в pre-bundling:

  • повторные старты становятся стабильнее;
  • уменьшается вероятность динамической переоптимизации.

optimizeDeps.exclude

Исключение модулей:

export default defineConfig({
  optimizeDeps: {
    exclude: ['my-large-lib']
  }
})

Неправильное исключение может ухудшить warm start, поскольку Vite будет обрабатывать модуль напрямую в runtime.


optimizeDeps.entries

Явное указание точек входа:

export default defineConfig({
  optimizeDeps: {
    entries: ['./src/main.js']
  }
})

Помогает сократить количество анализируемых файлов при старте.


Динамическая переоптимизация

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

Например:

const module = await import('some-library')

Если зависимость ранее не была обнаружена, Vite:

  1. Останавливает текущий процесс оптимизации.
  2. Пересобирает deps.
  3. Обновляет кеш.
  4. Перезапускает сервер.

Это ухудшает warm start.


Предотвращение переоптимизации

Для стабилизации запуска используются:

optimizeDeps.include

и:

optimizeDeps.entries

Особенно важно для:

  • монорепозиториев;
  • крупных React/Vue-приложений;
  • проектов с динамическими импортами;
  • plugin-based архитектур.

Тёплый старт и HMR

Warm start тесно связан с системой Hot Module Replacement.

После первого запуска Vite уже имеет:

  • граф зависимостей;
  • карту модулей;
  • сведения об импортерах;
  • подготовленные ESM-модули.

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

  • быстрее запускать watcher;
  • быстрее пересчитывать обновления;
  • ускорять HMR-цепочки.

Module Graph

Внутренний граф модулей — важнейший элемент производительности.

Vite хранит:

  • связи импортов;
  • обратные зависимости;
  • информацию о трансформациях;
  • статусы HMR.

Во время тёплого старта граф восстанавливается значительно быстрее.


Роль esbuild

Тёплый старт в Vite невозможен без esbuild.

Причины:

  • высокая скорость компиляции;
  • низкие накладные расходы;
  • быстрая обработка TypeScript;
  • мгновенный pre-bundling.

Даже крупные проекты получают быстрый повторный запуск благодаря esbuild-кешированию.


Warm start в больших проектах

В крупных приложениях разница особенно заметна.

Без кеша

Startup time: 12s

С тёплым стартом

Startup time: 700ms

На больших codebase ускорение может достигать десятков раз.


Монорепозитории

В monorepo warm start имеет особое значение.

Пример структуры:

apps/
packages/
shared/
tools/

Без оптимизации запуск может быть медленным из-за:

  • большого числа symlink;
  • workspace-пакетов;
  • перекрёстных зависимостей.

export default defineConfig({
  resolve: {
    preserveSymlinks: true
  }
})

Может влиять на стабильность кеширования в workspace-средах.


Общий кеш зависимостей

В некоторых инфраструктурах используют общий кеш между проектами.

Например:

~/.vite-cache

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

  • ускорять CI;
  • сокращать cold start;
  • уменьшать нагрузку на сборочные машины.

force и отключение warm start

Параметр:

vite --force

принудительно игнорирует кеш.

Поведение:

  • удаляется предыдущая оптимизация;
  • запускается новый pre-bundling;
  • создаётся новый dependency graph.

Используется при:

  • повреждении кеша;
  • странных HMR-ошибках;
  • обновлении плагинов;
  • миграции версий.

cacheDir

Каталог кеша можно изменить:

export default defineConfig({
  cacheDir: '.cache/vite'
})

Полезно для:

  • Docker;
  • CI/CD;
  • monorepo;
  • нестандартной структуры проекта.

Warm start в Docker

Контейнеры часто теряют кеш между перезапусками.

Проблема:

container restart → cold start

Решение — volume для кеша:

volumes:
  - vite-cache:/app/node_modules/.vite

Это сохраняет результаты pre-bundling.


CI/CD и кеширование

В CI warm start тоже может использоваться.

Кешируются:

node_modules
node_modules/.vite

Пример GitHub Actions:

- uses: actions/cache@v3
  with:
    path: |
      node_modules
      node_modules/.vite
    key: vite-${{ hashFiles('package-lock.json') }}

Влияние плагинов

Некоторые плагины ухудшают warm start.

Причины:

  • тяжёлые transform-хуки;
  • сложный AST-анализ;
  • синхронные файловые операции;
  • генерация виртуальных модулей.

Пример тяжёлого плагина

export default function MyPlugin() {
  return {
    name: 'heavy-plugin',

    transform(code, id) {
      // expensive work
      return code
    }
  }
}

Если transform выполняется для тысяч модулей, повторный старт замедляется.


Оптимизация плагинов

Для ускорения warm start применяются:

Кеширование transform-результатов

const cache = new Map()

Ограничение include/exclude

transform(code, id) {
  if (!id.endsWith('.js')) return
}

Ленивая обработка

Некоторые вычисления выполняются только при первом запросе модуля.


SSR и warm start

В SSR-проектах Vite использует отдельные механизмы кеширования.

Особенно важно для:

  • React SSR;
  • Nuxt;
  • SvelteKit;
  • custom middleware.

Warm start уменьшает время:

  • гидратации dev-сервера;
  • подготовки SSR entry;
  • анализа серверных модулей.

invalidate и сброс кеша

Иногда требуется ручная инвалидизация.

Примеры причин:

  • повреждение dependency graph;
  • конфликт версий;
  • ошибка виртуального модуля;
  • изменение plugin pipeline.

Очистка кеша

rm -rf node_modules/.vite

или:

vite --force

DEBUG-режим

Для анализа warm start используется:

DEBUG=vite:* vite

Vite выводит:

  • этапы оптимизации;
  • использование кеша;
  • dependency scan;
  • invalidation;
  • время pre-bundling.

Типичные проблемы тёплого старта

Повторная оптимизация при каждом запуске

Причины:

  • нестабильный lock-файл;
  • динамические зависимости;
  • плагины, меняющие конфигурацию;
  • временные файлы.

Медленный startup несмотря на кеш

Причины:

  • тяжёлые плагины;
  • огромный module graph;
  • большое число файлов;
  • медленный диск;
  • network filesystem.

Повреждённый кеш

Симптомы:

Failed to resolve dependency
Outdated optimize dep
504 optimize error

Решение:

vite --force

Архитектурное значение warm start

Тёплый старт — один из ключевых факторов популярности Vite.

Именно комбинация:

  • native ESM;
  • агрессивного кеширования;
  • esbuild;
  • dependency pre-bundling;
  • быстрого восстановления graph state;

обеспечивает почти мгновенный запуск dev-сервера даже в крупных приложениях.


Сравнение с webpack

В webpack dev-server warm start традиционно медленнее из-за:

  • полной bundle-модели;
  • сложной инкрементальной компиляции;
  • высокой нагрузки loader-системы.

Vite использует иной подход:

source files → native ESM → on-demand transform

Это резко уменьшает стоимость повторного запуска.


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

Стабилизировать lock-файлы

Частые изменения зависимостей ухудшают reuse кеша.


Минимизировать тяжёлые плагины

Особенно:

  • AST plugins;
  • Babel transforms;
  • synchronous I/O.

Использовать optimizeDeps.include

Для динамических импортов и больших библиотек.


Кешировать node_modules/.vite

Особенно в Docker и CI.


Использовать SSD

Warm start сильно зависит от скорости файловой системы.


Поведение при изменении исходников

Изменение обычных .js или .vue файлов:

  • не ломает warm start;
  • не требует нового pre-bundling;
  • обновляет только module graph.

Когда Vite теряет преимущества warm start

Наиболее проблемные сценарии:

  • тысячи динамических импортов;
  • огромные monorepo;
  • генерация модулей во время runtime;
  • нестабильные virtual modules;
  • сложные SSR pipelines;
  • плагины с полной перестройкой graph state.

Даже в этих случаях архитектура Vite обычно остаётся быстрее классических bundler-based решений.