Vite поддерживает несколько сред выполнения, каждая из которых ориентирована на собственную модель исполнения Jav * aScript:
browser — выполнение в браузере;node — выполнение в среде Node.js;edge — выполнение в edge-runtime окружениях.Режим запуска определяет:
Современные приложения редко ограничиваются одной средой выполнения. Один проект может одновременно содержать:
Именно поэтому в Vite появилась концепция разделения окружений исполнения.
browserbrowser — стандартное окружение Vite. Оно используется
для клиентского JavaScript, который выполняется внутри браузера.
Основные особенности:
Именно этот режим активен по умолчанию в большинстве проектов.
В браузерном окружении доступны:
window
document
navigator
location
history
fetch
localStorage
sessionStorage
Пример:
console.log(window.innerWidth)
document.body.innerHTML = '<h1>Hello</h1>'
В browser-режиме Vite не ограничивает взаимодействие с
DOM.
Пример:
const button = document.querySelector('button')
button.addEventListener('click', () => {
console.log('clicked')
})
Только браузерный режим поддерживает полноценную работу со стилями:
import './style.css'
Vite:
Hot Module Replacement — одна из ключевых возможностей Vite.
Пример:
if (import.meta.hot) {
import.meta.hot.accept((module) => {
console.log('module updated')
})
}
HMR работает именно через браузерную инфраструктуру:
Браузер не предоставляет доступ к:
Следующий код работать не будет:
import fs from 'fs'
Ошибка:
Module "fs" has been externalized
Vite ориентирован на native ESM браузеров.
Пример:
import { sum } from './math.js'
console.log(sum(1, 2))
Во время разработки Vite:
Несмотря на использование ESM, многие npm-пакеты распространяются в CommonJS.
Vite выполняет pre-bundling:
node_modules → esbuild → optimized deps
Это необходимо для:
В production Vite автоматически разбивает код:
const module = await import('./feature.js')
Результат:
nodenode предназначен для выполнения кода в среде
Node.js.
Типичные сценарии:
В Node.js доступны:
process
Buffer
fs
path
os
http
stream
crypto
Пример:
import fs from 'node:fs'
const content = fs.readFileSync('./file.txt', 'utf8')
console.log(content)
SSR — главный сценарий использования node-режима.
Пример:
export async function render(url) {
return `
<html>
<body>
<h1>${url}</h1>
</body>
</html>
`
}
Vite способен запускать серверный код отдельно от клиентского.
Обычно структура выглядит так:
src/
client/
server/
Клиентский код:
document.body.innerHTML = 'client'
Серверный код:
import fs from 'node:fs'
При SSR Vite старается не бандлить зависимости без необходимости.
Например:
import express from 'express'
Пакет может быть externalized:
require('express')
Это ускоряет серверный запуск.
Иногда пакет должен пройти через Vite-трансформации.
Пример:
export default defineConfig({
ssr: {
noExternal: ['some-package']
}
})
Причины:
Node.js долгое время строился вокруг CommonJS.
Vite поддерживает:
Пример CommonJS:
const fs = require('fs')
Пример ESM:
import fs from 'node:fs'
В Node.js доступны системные переменные:
process.env.PORT
В Vite также существуют:
import.meta.env
Разница принципиальна:
| API | Где доступно |
|---|---|
| process.env | Node.js |
| import.meta.env | Browser + Vite |
Vite может работать как middleware внутри Node.js сервера.
Пример:
import express from 'express'
import { createServer } from 'vite'
const app = express()
const vite = await createServer({
server: {
middlewareMode: true
}
})
app.use(vite.middlewares)
app.listen(3000)
Многие meta-framework используют Node-режим для анализа файловой системы.
Пример:
pages/
index.jsx
about.jsx
Сервер:
В SSR возможно потоковое рендеринг.
Пример:
response.write('<html>')
response.write('<body>')
response.end('</body></html>')
Браузерный режим подобного не поддерживает.
edgeedge — промежуточная среда между браузером и
Node.js.
Edge runtime используется:
Примеры платформ:
Код выполняется:
Обычно отсутствуют:
fs
net
tls
child_process
http server
Пример неработающего кода:
import fs from 'node:fs'
Edge-runtime ориентируется на Web Standards.
Доступны:
fetch
Request
Response
Headers
URL
ReadableStream
crypto.subtle
Пример:
export default async function handler(request) {
return new Response('hello')
}
Большинство edge-платформ используют fetch как
основу.
Пример:
addEventListener('fetch', event => {
event.respondWith(handle(event.request))
})
Или:
export default {
async fetch(request) {
return new Response('edge')
}
}
Vite может собирать код под edge-runtime:
| Возможность | node | edge |
|---|---|---|
| fs | Да | Нет |
| TCP sockets | Да | Нет |
| DOM | Нет | Нет |
| fetch | Да | Да |
| Streams | Да | Да |
| process | Да | Частично |
| Buffer | Да | Частично |
| ultra-low latency | Ограниченно | Да |
Edge-runtime создавался как решение проблемы холодного старта serverless.
Преимущества:
Edge-функции требуют минимального размера сборки.
Поэтому Vite:
Многие edge-runtime поддерживают только ESM.
CommonJS:
const x = require('x')
часто не работает.
Необходим ESM:
import x from 'x'
Edge отлично подходит для потоковых ответов.
Пример:
const stream = new ReadableStream({
start(controller) {
controller.enqueue('hello')
controller.close()
}
})
return new Response(stream)
| Возможность | browser | node | edge |
|---|---|---|---|
| DOM | Да | Нет | Нет |
| fs | Нет | Да | Нет |
| process | Нет | Да | Частично |
| fetch | Да | Да | Да |
| Streams | Частично | Да | Да |
| HMR | Да | Нет | Нет |
| SSR | Нет | Да | Ограниченно |
| Web API | Да | Частично | Да |
| TCP sockets | Нет | Да | Нет |
| CDN execution | Нет | Нет | Да |
Подходит для:
Подходит для:
Подходит для:
Не весь JavaScript переносим между окружениями.
Пример:
if (typeof window !== 'undefined') {
console.log('browser')
}
const isBrowser =
typeof window !== 'undefined'
const isNode =
typeof process !== 'undefined'
const isEdge =
typeof EdgeRuntime !== 'undefined'
Vite предоставляет специальный флаг:
if (import.meta.env.SSR) {
console.log('server')
}
Пример:
if (!import.meta.env.SSR) {
document.body.innerHTML = 'client'
}
export default defineConfig({
ssr: {
target: 'node'
}
})
export default defineConfig({
ssr: {
target: 'webworker'
}
})
webworker используется как основа для edge-runtime.
Часто используются две сборки:
vite build
vite build --ssr
Результат:
dist/client
dist/server
Пример:
if (import.meta.env.SSR) {
const fs = await import('node:fs')
}
if (typeof window !== 'undefined') {
import('./browser.js')
}
if (import.meta.env.SSR) {
import('./server.js')
}
Некоторые Node API могут полифилиться:
Buffer
process
path
Но это увеличивает bundle size.
Edge-runtime поддерживает меньше polyfills.
Многие Node-модули вообще невозможно перенести:
fs
child_process
cluster
Обычно код разделяется:
src/
browser/
server/
edge/
shared/
Общий код:
export function sum(a, b) {
return a + b
}
Распространенный подход:
adapters/
browser.js
node.js
edge.js
Пример:
export async function getStorage() {
if (import.meta.env.SSR) {
return nodeStorage
}
return browserStorage
}
Edge-runtime концептуально ближе к Web Worker.
Поэтому отсутствуют:
window
document
Но доступны:
self
fetch
Response
Большие npm-пакеты плохо подходят для edge.
Предпочтительны:
Edge-функции часто имеют ограничения:
Из-за этого нежелательны:
Наиболее распространенная модель:
Browser → Node SSR → HTML
Edge SSR:
Browser → Edge Location → HTML
Преимущество:
После SSR браузер гидратирует приложение:
SSR HTML → Browser JS → Interactive App
Это работает независимо от того, где был выполнен SSR:
Некоторые библиотеки:
Пример проблемного кода:
import fs from 'node:fs'
или:
document.querySelector()
Изоморфные библиотеки работают сразу в нескольких средах.
Пример:
export async function request(url) {
return fetch(url)
}
Многие библиотеки проверяют окружение:
const isBrowser =
typeof window !== 'undefined'
или:
const isNode =
typeof process !== 'undefined'
Современная экосистема JavaScript движется в сторону:
Vite проектировался именно под такую архитектуру: