Параметр server.middlewareMode в конфигурации Vite
переводит dev-сервер в режим middleware. В обычном режиме Vite
самостоятельно запускает HTTP-сервер, обрабатывает запросы браузера,
раздаёт модули, управляет HMR и открывает страницу приложения. В режиме
middleware Vite перестаёт быть самостоятельным сервером и начинает
работать как промежуточный обработчик внутри другого
Node.js-сервера.
Такой режим используется при интеграции Vite с:
Главная идея заключается в том, что HTTP-сервер создаётся вручную, а Vite подключается к нему как middleware.
В обычном режиме:
Браузер → Vite Server
В middleware-режиме:
Браузер → Express/Koa/Fastify → Vite Middleware
Это позволяет:
Пример конфигурации:
import { defineConfig } from 'vite'
export default defineConfig({
server: {
middlewareMode: true
}
})
После включения параметра:
listen();Самый распространённый вариант — подключение Vite к Express.
npm install express vite
import express from 'express'
import { createServer as createViteServer } from 'vite'
async function createServer() {
const app = express()
const vite = await createViteServer({
server: {
middlewareMode: true
}
})
app.use(vite.middlewares)
app.listen(3000)
}
createServer()
В этом примере:
vite.middlewares;vite.middlewaresСвойство:
vite.middlewares
представляет собой готовый middleware-объект Connect.
Он включает:
/@vite/client;Фактически это внутренний dev-сервер Vite, встроенный в приложение.
middlewareMode особенно важен для SSR.
Пример:
import fs from 'fs'
import path from 'path'
import express from 'express'
import { createServer as createViteServer } from 'vite'
async function createServer() {
const app = express()
const vite = await createViteServer({
server: {
middlewareMode: true
},
appType: 'custom'
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
try {
const url = req.originalUrl
let template = fs.readFileSync(
path.resolve('index.html'),
'utf-8'
)
template = await vite.transformIndexHtml(url, template)
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
const appHtml = await render(url)
const html = template.replace(
'<!--app-html-->',
appHtml
)
res.status(200).set({
'Content-Type': 'text/html'
}).end(html)
} catch (e) {
vite.ssrFixStacktrace(e)
console.error(e)
res.status(500).end(e.message)
}
})
app.listen(3000)
}
createServer()
appType: 'custom'При SSR почти всегда используется:
appType: 'custom'
Это отключает встроенную HTML-обработку Vite.
Без этого параметра:
При запросе страницы происходит следующая цепочка:
1. Express получает запрос
2. Vite обрабатывает middleware
3. Загружается HTML-шаблон
4. Vite трансформирует HTML
5. Загружается SSR-модуль
6. React/Vue/Svelte рендерят HTML
7. HTML отправляется браузеру
transformIndexHtmlМетод:
vite.transformIndexHtml(url, html)
обрабатывает HTML через pipeline Vite.
Он:
Пример:
const transformed = await vite.transformIndexHtml(
'/',
template
)
ssrLoadModuleМетод:
vite.ssrLoadModule()
загружает модуль напрямую через Vite.
Пример:
const module = await vite.ssrLoadModule(
'/src/entry-server.js'
)
Особенности:
import()Обычный:
await import('./module.js')
использует Node.js loader.
vite.ssrLoadModule():
Пример:
import Koa from 'koa'
import { createServer } from 'vite'
async function bootstrap() {
const app = new Koa()
const vite = await createServer({
server: {
middlewareMode: true
}
})
app.use(async (ctx, next) => {
await vite.middlewares(ctx.req, ctx.res, next)
})
app.listen(3000)
}
bootstrap()
Пример:
import Fastify from 'fastify'
import middie from '@fastify/middie'
import { createServer } from 'vite'
async function bootstrap() {
const app = Fastify()
await app.register(middie)
const vite = await createServer({
server: {
middlewareMode: true
}
})
app.use(vite.middlewares)
await app.listen({
port: 3000
})
}
bootstrap()
Vite часто применяется как dev-layer поверх NestJS.
Пример:
const vite = await createViteServer({
server: {
middlewareMode: true
}
})
app.use(vite.middlewares)
Это позволяет:
Параметр может принимать:
middlewareMode: true
или:
middlewareMode: 'ssr'
true и
'ssr'trueПолный middleware-режим.
Включает:
'ssr'SSR-only режим.
Используется для специализированных SSR-сценариев.
Часто применяется framework-авторами.
'ssr'Режим 'ssr' полезен:
Hot Module Replacement продолжает работать полностью.
Vite автоматически:
Даже если сервер полностью кастомный, HMR остаётся активным.
Иногда требуется отдельная настройка HMR:
server: {
middlewareMode: true,
hmr: {
port: 24678
}
}
Это полезно:
Обычно backend и frontend объединяются:
app.get('/api/users', (req, res) => {
res.json(users)
})
app.use(vite.middlewares)
Либо наоборот:
app.use(vite.middlewares)
app.get('/api/users', handler)
Порядок middleware влияет на маршрутизацию.
Порядок критически важен.
Пример:
app.use(customMiddleware)
app.use(vite.middlewares)
app.use(apiMiddleware)
Каждый middleware получает запрос по цепочке.
Неправильный порядок может привести к:
app.use((req, res, next) => {
console.log(req.url)
next()
})
app.use(vite.middlewares)
Такой подход часто используется для:
Прокси продолжают работать:
server: {
middlewareMode: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
Режим middleware особенно популярен в Docker-средах.
Причины:
В monorepo middleware-режим помогает:
Необходимо самостоятельно:
listen();Разработчик самостоятельно отвечает за:
MiddlewareMode значительно сложнее обычного:
npm create vite@latest
Он предназначен прежде всего для:
HTTPS настраивается уже на основном сервере:
https.createServer(options, app)
а не через встроенный Vite server.
Можно использовать собственные websocket-серверы:
const server = http.createServer(app)
const io = new Server(server)
при этом Vite HMR продолжит работать параллельно.
При SSR важно использовать:
vite.ssrFixStacktrace(error)
Это исправляет stack trace:
Без этого stack trace часто указывает на временные transformed-файлы Vite.
В production middlewareMode обычно заменяется готовым bundle.
Типичная схема:
Express + Vite middleware
Express + dist/client + dist/server
Часто используется:
const isProd = process.env.NODE_ENV === 'production'
Далее:
if (!isProd) {
const vite = await createViteServer({
server: {
middlewareMode: true
}
})
app.use(vite.middlewares)
}
Именно middlewareMode сделал возможным:
Он превращает Vite из standalone dev-сервера в полноценный frontend runtime engine внутри серверного приложения.