Экстернализация node-зависимостей в Vite в контексте SSR представляет собой механизм управления тем, какие модули должны попадать в серверный бандл, а какие должны оставаться внешними и загружаться напрямую из среды выполнения Node.js. Этот процесс критичен для производительности, корректности резолва модулей и совместимости между ESM и CommonJS.
При серверном рендеринге приложение выполняется не в браузере, а в среде Node.js. Это накладывает принципиально иные ограничения:
В SSR-сборке Vite формирует отдельный серверный бандл. В этот момент возникает ключевая проблема: часть зависимостей выгодно бандлить, а часть — оставлять внешними (external).
Экстернализация означает, что модуль не включается в итоговый
SSR-бандл, а заменяется на require() или
import во время выполнения.
Некоторые пакеты рассчитывают на нативное поведение Node.js:
require__dirnameБандлинг таких модулей может ломать их поведение.
SSR-бандл часто выполняется на сервере при каждом запросе или при горячем обновлении. Избыточное включение зависимостей:
Многие зависимости уже поставляются оптимизированными для Node.js. Повторная упаковка:
Основной механизм управления экстернализацией в Vite — параметр:
export default {
ssr: {
external: []
}
}
По умолчанию Vite стремится автоматически внешними делать большинство
node_modules, если они не требуют трансформации.
ssr.external позволяет явно указать пакеты, которые
должны быть исключены из бандла:
ssr: {
external: ['lodash', 'express']
}
Это приводит к тому, что:
Противоположный механизм — ssr.noExternal.
export default {
ssr: {
noExternal: ['some-esm-package']
}
}
Он принудительно заставляет Vite включить пакет в SSR-бандл.
Некоторые пакеты:
В таких случаях экстернализация приводит к runtime error:
Пакет написан под ESM, но Node.js проект использует смешанный режим:
Тогда noExternal заставляет Vite обработать пакет как
часть графа сборки.
Vite применяет эвристики:
Большинство зависимостей не бандлятся в SSR, если не требуется трансформация.
Если пакет:
Vite может включить его в бандл.
Vite строит два графа:
И решения по external принимаются отдельно.
Vite имеет отдельный механизм dependency pre-bundling (optimizeDeps), который относится к dev-клиенту. SSR external работает независимо, но конфликты возникают:
Результат:
В SSR автоматически external становятся встроенные модули Node.js:
Это поведение важно для предотвращения их попадания в бандл. Их бандлинг невозможен или бессмысленен, так как они предоставляются runtime.
Используется в backend-heavy SSR:
ssr: {
external: [
'pg',
'mysql2',
'redis',
'mongoose'
]
}
Цель — оставить тяжёлые зависимости вне бандла.
ssr: {
noExternal: [
'@scope/ui-components'
]
}
Применяется для UI библиотек, которые:
Комбинация:
Некоторые пакеты имеют:
Vite может выбрать не тот entrypoint при external, если package.json exports сложный.
Пакеты с CJS + ESM могут приводить к:
В pnpm/yarn workspaces:
Экстернализация напрямую влияет на:
Бандлинг уменьшает число файлов, но увеличивает CPU cost на сборку. External снижает CPU, но увеличивает runtime resolution cost.
Баланс зависит от:
Типовые ошибки:
Диагностика обычно начинается с анализа:
Особенно критичны пакеты с:
Экстернализация в SSR Vite можно рассматривать как трёхслойную систему:
Она работает поверх Node.js resolution, сохраняя баланс между: