Vite поддерживает импорт Web Worker и SharedWorker напрямую через механизм специальных суффиксов в пути импорта. Вместо ручного создания отдельных файлов, настройки сборщика и генерации URL используется декларативный синтаксис:
import MyWorker from './worker.js?worker'
import MySharedWorker from './shared.js?sharedworker'
После такого импорта переменная содержит не модуль, а конструктор
воркера, который можно использовать через new.
Пример:
import MyWorker from './worker.js?worker'
const worker = new MyWorker()
worker.postMessage('Hello')
Vite автоматически:
Web Worker — отдельный поток выполнения JavaScript, работающий параллельно основному UI-потоку браузера.
Воркеры используются для:
Главная особенность — отсутствие доступа к DOM.
?workersrc/
├── main.js
└── worker.js
self.onmess age = (event) => {
const result = event.data * 2
self.postMessage(result)
}
import MyWorker from './worker.js?worker'
const worker = new MyWorker()
worker.postMessage(10)
worker.onmess age = (event) => {
console.log(event.data)
}
Результат:
20
?workerИмпорт:
import WorkerModule from './worker.js?worker'
эквивалентен примерно следующему коду:
const worker = new Worker(
new URL('./worker.js', import.meta.url),
{ type: 'module' }
)
Однако Vite:
Vite создаёт worker в формате ES Module.
Это означает возможность использовать:
import
export
top-level await
dynamic import()
Пример:
import { sum } from './math.js'
self.onmess age = (e) => {
self.postMessage(sum(e.data.a, e.data.b))
}
Воркеры в Vite работают как полноценные модули.
import dayjs from 'dayjs'
self.onmess age = () => {
self.postMessage(dayjs().format())
}
Vite включит библиотеку в worker-bundle автоматически.
Обмен выполняется через:
postMessage()
и обработчики:
onmessage
worker.postMessage({
numbers: [1, 2, 3]
})
self.onmess age = (event) => {
console.log(event.data)
}
self.postMessage({
status: 'done'
})
selfВнутри воркера глобальным объектом является:
self
Он аналогичен window в основном потоке.
Пример:
self.addEventListener('message', (event) => {
self.postMessage(event.data)
})
worker.terminate()
После этого worker уничтожается полностью.
self.close()
Воркеры особенно эффективны при работе с:
const buffer = new ArrayBuffer(1024)
worker.postMessage(buffer, [buffer])
Второй аргумент — transferable objects.
Вместо копирования память передаётся между потоками.
?worker&inlineVite поддерживает inline-режим:
import MyWorker from './worker.js?worker&inline'
В этом случае worker встраивается в основной bundle.
Обычно используется:
import W from './worker.js?worker'
Особенности:
import W from './worker.js?worker&inline'
Особенности:
?worker&urlИногда нужен только URL worker-файла.
Для этого используется:
import workerUrl from './worker.js?worker&url'
Пример:
const worker = new Worker(workerUrl, {
type: 'module'
})
SharedWorker — специальный тип воркера, который может
использоваться несколькими вкладками, окнами или iframe
одновременно.
Импорт:
import SharedWorkerModule from './shared.js?sharedworker'
Создание:
const worker = new SharedWorkerModule()
const connections = []
self.onconn ect = (event) => {
const port = event.ports[0]
connections.push(port)
port.onmess age = (e) => {
for (const connection of connections) {
connection.postMessage(e.data)
}
}
port.start()
}
import MySharedWorker from './shared.js?sharedworker'
const worker = new MySharedWorker()
worker.port.postMessage('Hello')
worker.port.onmess age = (e) => {
console.log(e.data)
}
Вместо:
onmessage
postMessage
используется:
port.onmessage
port.postMessage
Причина — SharedWorker поддерживает несколько подключений одновременно.
Vite поддерживает worker-модули в TypeScript без дополнительной настройки.
self.onmess age = (event: MessageEvent<number>) => {
self.postMessage(event.data * 2)
}
import MyWorker from './worker.ts?worker'
const worker = new MyWorker()
Можно описать тип сообщения:
type Request = {
value: number
}
type Response = {
result: number
}
self.onmess age = (event: MessageEvent<Request>) => {
self.postMessage({
result: event.data.value * 2
} satisfies Response)
}
new URL() без ?workerVite также поддерживает стандартный вариант:
new Worker(
new URL('./worker.js', import.meta.url),
{
type: 'module'
}
)
Но ?worker обычно предпочтительнее из-за:
Воркеры можно загружать динамически.
const { default: MyWorker } =
await import('./worker.js?worker')
const worker = new MyWorker()
Полезно для:
worker.oner ror = (event) => {
console.error(event.message)
}
throw new Error('Worker failed')
Возможны при:
Во время разработки Vite автоматически обновляет worker-модули.
Изменения в:
worker.js
приводят к пересборке worker bundle.
Воркеры не имеют доступа к:
Поддерживаются:
Vite позволяет импортировать WebAssembly в worker.
import init from './math.wasm'
Это особенно полезно для:
Worker не ускоряет JavaScript автоматически.
Преимущество достигается за счёт:
Использование worker может быть избыточным для:
Создание worker тоже имеет стоимость:
UI → Worker
Подходит для:
UI → Pool → Workers
Используется для:
Tabs ↔ SharedWorker ↔ Server
Применяется для:
При vite build:
Пример:
assets/worker-8f3a1d.js
Поддерживается практически всеми современными браузерами.
Поддержка хуже, особенно:
При использовании SharedWorker желательно проверять совместимость.
function fib(n) {
if (n <= 1) {
return n
}
return fib(n - 1) + fib(n - 2)
}
self.onmess age = (e) => {
const result = fib(e.data)
self.postMessage(result)
}
import FibWorker from './fibonacci-worker.js?worker'
const worker = new FibWorker()
worker.postMessage(40)
worker.onmess age = (e) => {
console.log('Result:', e.data)
}
Без worker интерфейс браузера мог бы зависнуть во время вычислений.
self.onmess age = (e) => {
const parsed = JSON.parse(e.data)
self.postMessage(parsed)
}
import JsonWorker from './json-worker.js?worker'
const worker = new JsonWorker()
fetch('/large.json')
.then((r) => r.text())
.then((text) => {
worker.postMessage(text)
})
worker.onmess age = (e) => {
console.log(e.data)
}
self.onmess age = (e) => {
const pixels = e.data
for (let i = 0; i < pixels.length; i += 4) {
pixels[i] = 255 - pixels[i]
pixels[i + 1] = 255 - pixels[i + 1]
pixels[i + 2] = 255 - pixels[i + 2]
}
self.postMessage(pixels)
}
Такой подход часто используется в: