Режимы запуска: browser, node, edge

Vite поддерживает несколько сред выполнения, каждая из которых ориентирована на собственную модель исполнения Jav * aScript:

  • browser — выполнение в браузере;
  • node — выполнение в среде Node.js;
  • edge — выполнение в edge-runtime окружениях.

Режим запуска определяет:

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

Современные приложения редко ограничиваются одной средой выполнения. Один проект может одновременно содержать:

  • клиентский код для браузера;
  • серверный код на Node.js;
  • edge-функции для CDN или serverless-платформ.

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


Режим browser

Назначение

browser — стандартное окружение Vite. Оно используется для клиентского JavaScript, который выполняется внутри браузера.

Основные особенности:

  • доступ к DOM;
  • использование Web API;
  • HMR;
  • загрузка модулей через ESM;
  • работа с CSS;
  • поддержка frontend-фреймворков.

Именно этот режим активен по умолчанию в большинстве проектов.


Доступные глобальные объекты

В браузерном окружении доступны:

window
document
navigator
location
history
fetch
localStorage
sessionStorage

Пример:

console.log(window.innerWidth)

document.body.innerHTML = '<h1>Hello</h1>'

Работа с DOM

В browser-режиме Vite не ограничивает взаимодействие с DOM.

Пример:

const button = document.querySelector('button')

button.addEventListener('click', () => {
    console.log('clicked')
})

Подключение CSS

Только браузерный режим поддерживает полноценную работу со стилями:

import './style.css'

Vite:

  • отслеживает CSS через HMR;
  • внедряет стили без полной перезагрузки страницы;
  • поддерживает PostCSS;
  • поддерживает CSS Modules;
  • поддерживает preprocessors.

HMR в browser

Hot Module Replacement — одна из ключевых возможностей Vite.

Пример:

if (import.meta.hot) {
    import.meta.hot.accept((module) => {
        console.log('module updated')
    })
}

HMR работает именно через браузерную инфраструктуру:

  • websocket-соединение;
  • динамическая замена модулей;
  • повторная отрисовка компонентов.

Ограничения browser-режима

Браузер не предоставляет доступ к:

  • файловой системе;
  • процессам ОС;
  • TCP/UDP;
  • системным сокетам;
  • Node.js API.

Следующий код работать не будет:

import fs from 'fs'

Ошибка:

Module "fs" has been externalized

Browser и ESM

Vite ориентирован на native ESM браузеров.

Пример:

import { sum } from './math.js'

console.log(sum(1, 2))

Во время разработки Vite:

  • не собирает весь проект заранее;
  • отдает модули по запросу;
  • использует ES-модули напрямую.

Browser и оптимизация зависимостей

Несмотря на использование ESM, многие npm-пакеты распространяются в CommonJS.

Vite выполняет pre-bundling:

node_modules → esbuild → optimized deps

Это необходимо для:

  • ускорения dev server;
  • уменьшения количества HTTP-запросов;
  • преобразования CommonJS в ESM.

Browser и code splitting

В production Vite автоматически разбивает код:

const module = await import('./feature.js')

Результат:

  • отдельный chunk;
  • ленивое подключение;
  • уменьшение initial bundle.

Режим node

Назначение

node предназначен для выполнения кода в среде Node.js.

Типичные сценарии:

  • SSR;
  • backend;
  • CLI;
  • API-серверы;
  • генераторы статических сайтов;
  • middleware;
  • build-инструменты.

Доступные API

В 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)

Server Side Rendering

SSR — главный сценарий использования node-режима.

Пример:

export async function render(url) {
    return `
        <html>
            <body>
                <h1>${url}</h1>
            </body>
        </html>
    `
}

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


Разделение client/server

Обычно структура выглядит так:

src/
    client/
    server/

Клиентский код:

document.body.innerHTML = 'client'

Серверный код:

import fs from 'node:fs'

Node и SSR externalization

При SSR Vite старается не бандлить зависимости без необходимости.

Например:

import express from 'express'

Пакет может быть externalized:

require('express')

Это ускоряет серверный запуск.


SSR noExternal

Иногда пакет должен пройти через Vite-трансформации.

Пример:

export default defineConfig({
    ssr: {
        noExternal: ['some-package']
    }
})

Причины:

  • пакет содержит ESM-only код;
  • пакет использует нестандартный синтаксис;
  • пакет требует обработки Vite.

Node и CommonJS

Node.js долгое время строился вокруг CommonJS.

Vite поддерживает:

  • ESM;
  • CommonJS;
  • смешанные зависимости.

Пример CommonJS:

const fs = require('fs')

Пример ESM:

import fs from 'node:fs'

Node и environment variables

В Node.js доступны системные переменные:

process.env.PORT

В Vite также существуют:

import.meta.env

Разница принципиальна:

API Где доступно
process.env Node.js
import.meta.env Browser + Vite

Node и middleware mode

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)

Node и file system routing

Многие meta-framework используют Node-режим для анализа файловой системы.

Пример:

pages/
    index.jsx
    about.jsx

Сервер:

  • сканирует директории;
  • строит роуты;
  • генерирует manifest.

Node и streaming

В SSR возможно потоковое рендеринг.

Пример:

response.write('<html>')
response.write('<body>')
response.end('</body></html>')

Браузерный режим подобного не поддерживает.


Режим edge

Концепция edge-runtime

edge — промежуточная среда между браузером и Node.js.

Edge runtime используется:

  • CDN-платформами;
  • serverless edge-функциями;
  • распределенными вычислениями;
  • ultra-low latency приложениями.

Примеры платформ:

  • Cloudflare Workers;
  • Vercel Edge Functions;
  • Netlify Edge;
  • Deno Deploy.

Главная идея edge

Код выполняется:

  • не в браузере;
  • не в полноценном Node.js;
  • а внутри изолированного lightweight runtime.

Ограничения edge-runtime

Обычно отсутствуют:

fs
net
tls
child_process
http server

Пример неработающего кода:

import fs from 'node:fs'

Web API вместо Node API

Edge-runtime ориентируется на Web Standards.

Доступны:

fetch
Request
Response
Headers
URL
ReadableStream
crypto.subtle

Пример:

export default async function handler(request) {
    return new Response('hello')
}

Edge и fetch-first архитектура

Большинство edge-платформ используют fetch как основу.

Пример:

addEventListener('fetch', event => {
    event.respondWith(handle(event.request))
})

Или:

export default {
    async fetch(request) {
        return new Response('edge')
    }
}

Edge и Vite

Vite может собирать код под edge-runtime:

  • без Node.js built-ins;
  • с учетом ограничений платформы;
  • с правильным target;
  • с корректной трансформацией модулей.

Отличия edge от node

Возможность node edge
fs Да Нет
TCP sockets Да Нет
DOM Нет Нет
fetch Да Да
Streams Да Да
process Да Частично
Buffer Да Частично
ultra-low latency Ограниченно Да

Edge и cold start

Edge-runtime создавался как решение проблемы холодного старта serverless.

Преимущества:

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

Edge и bundle size

Edge-функции требуют минимального размера сборки.

Поэтому Vite:

  • aggressively tree-shakes код;
  • минимизирует зависимости;
  • исключает Node built-ins.

Edge и ESM-only

Многие edge-runtime поддерживают только ESM.

CommonJS:

const x = require('x')

часто не работает.

Необходим ESM:

import x from 'x'

Edge и streaming response

Edge отлично подходит для потоковых ответов.

Пример:

const stream = new ReadableStream({
    start(controller) {
        controller.enqueue('hello')
        controller.close()
    }
})

return new Response(stream)

Сравнение browser, node и edge

Возможность browser node edge
DOM Да Нет Нет
fs Нет Да Нет
process Нет Да Частично
fetch Да Да Да
Streams Частично Да Да
HMR Да Нет Нет
SSR Нет Да Ограниченно
Web API Да Частично Да
TCP sockets Нет Да Нет
CDN execution Нет Нет Да

Выбор режима запуска

Когда использовать browser

Подходит для:

  • SPA;
  • frontend;
  • UI;
  • интерактивных приложений;
  • клиентской логики.

Когда использовать node

Подходит для:

  • SSR;
  • backend;
  • API;
  • middleware;
  • генерации HTML;
  • build-систем.

Когда использовать edge

Подходит для:

  • геораспределенного SSR;
  • CDN logic;
  • authentication middleware;
  • edge caching;
  • ultra-fast response;
  • personalization near user.

Универсальный код

Проблема cross-runtime

Не весь JavaScript переносим между окружениями.

Пример:

if (typeof window !== 'undefined') {
    console.log('browser')
}

Проверка среды

Browser

const isBrowser =
    typeof window !== 'undefined'

Node

const isNode =
    typeof process !== 'undefined'

Edge

const isEdge =
    typeof EdgeRuntime !== 'undefined'

import.meta.env.SSR

Vite предоставляет специальный флаг:

if (import.meta.env.SSR) {
    console.log('server')
}

Пример:

if (!import.meta.env.SSR) {
    document.body.innerHTML = 'client'
}

Конфигурация Vite для разных runtime

SSR target

export default defineConfig({
    ssr: {
        target: 'node'
    }
})

Edge target

export default defineConfig({
    ssr: {
        target: 'webworker'
    }
})

webworker используется как основа для edge-runtime.


Раздельные сборки

Часто используются две сборки:

vite build
vite build --ssr

Результат:

dist/client
dist/server

Runtime-specific imports

Условные импорты

Пример:

if (import.meta.env.SSR) {
    const fs = await import('node:fs')
}

Исключение browser-only кода

if (typeof window !== 'undefined') {
    import('./browser.js')
}

Исключение node-only кода

if (import.meta.env.SSR) {
    import('./server.js')
}

Polyfills

Browser polyfills

Некоторые Node API могут полифилиться:

Buffer
process
path

Но это увеличивает bundle size.


Edge polyfills

Edge-runtime поддерживает меньше polyfills.

Многие Node-модули вообще невозможно перенести:

fs
child_process
cluster

Runtime-aware архитектура

Изоляция окружений

Обычно код разделяется:

src/
    browser/
    server/
    edge/
    shared/

Shared modules

Общий код:

export function sum(a, b) {
    return a + b
}

Runtime adapters

Распространенный подход:

adapters/
    browser.js
    node.js
    edge.js

Абстракция платформы

Пример:

export async function getStorage() {
    if (import.meta.env.SSR) {
        return nodeStorage
    }

    return browserStorage
}

Особенности сборки под edge

Web Worker target

Edge-runtime концептуально ближе к Web Worker.

Поэтому отсутствуют:

window
document

Но доступны:

self
fetch
Response

Минимизация зависимостей

Большие npm-пакеты плохо подходят для edge.

Предпочтительны:

  • lightweight libraries;
  • ESM-only packages;
  • zero-dependency решения.

Ограничения времени выполнения

Edge-функции часто имеют ограничения:

  • CPU time;
  • memory;
  • execution duration.

Из-за этого нежелательны:

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

SSR и runtime

SSR в node

Наиболее распространенная модель:

Browser → Node SSR → HTML

SSR в edge

Edge SSR:

Browser → Edge Location → HTML

Преимущество:

  • минимальная задержка;
  • рендер ближе к пользователю.

Hydration

После SSR браузер гидратирует приложение:

SSR HTML → Browser JS → Interactive App

Это работает независимо от того, где был выполнен SSR:

  • node;
  • edge.

Runtime compatibility

Проблемы совместимости

Некоторые библиотеки:

  • ориентированы только на browser;
  • используют Node API;
  • не поддерживают edge.

Пример проблемного кода:

import fs from 'node:fs'

или:

document.querySelector()

Isomorphic libraries

Изоморфные библиотеки работают сразу в нескольких средах.

Пример:

export async function request(url) {
    return fetch(url)
}

Runtime detection inside libraries

Многие библиотеки проверяют окружение:

const isBrowser =
    typeof window !== 'undefined'

или:

const isNode =
    typeof process !== 'undefined'

Современная тенденция

Современная экосистема JavaScript движется в сторону:

  • ESM;
  • Web API;
  • runtime-agnostic кода;
  • edge-compatible библиотек;
  • universal rendering.

Vite проектировался именно под такую архитектуру:

  • browser-first development;
  • быстрый dev server;
  • native ESM;
  • multi-runtime execution;
  • SSR;
  • edge deployment;
  • универсальные сборки.