Shared Workers представляют собой тип веб-воркеров, предназначенных для совместного использования одного фонового JavaScript-контекста несколькими вкладками, окнами или iframe, происходящими из одного источника. В отличие от классических Dedicated Worker, которые привязаны к одной странице, Shared Worker функционирует как общий сервисный процесс внутри браузера, принимающий соединения от множества клиентов и обеспечивающий централизованную обработку данных.
В контексте Parcel работа с Shared Workers сводится не только к написанию самого воркера, но и к корректной сборке, разделению бандлов, поддержке модульной структуры и интеграции с системой HMR (Hot Module Replacement). Parcel автоматически определяет воркеры как отдельные точки входа и формирует для них независимые бандлы, сохраняя при этом связь с основным приложением.
Shared Worker существует в единственном экземпляре на уровень origin.
При создании нового подключения через new SharedWorker()
браузер проверяет, существует ли уже активный воркер с таким же URL.
Если существует — добавляется новое соединение, если нет — создаётся
новый процесс.
Ключевой механизм взаимодействия строится на портах:
MessagePortpostMessageShared Worker не имеет доступа к DOM, но может использовать:
Файл воркера представляет собой отдельный модуль:
// shared-worker.js
const connections = new Set();
self.onconn ect = (event) => {
const port = event.ports[0];
connections.add(port);
port.start();
port.onmess age = (e) => {
const message = e.data;
for (const client of connections) {
client.postMessage({
type: 'broadcast',
payload: message,
});
}
};
port.postMessage({
type: 'init',
payload: 'Shared Worker подключён',
});
};
Особенность модели заключается в том, что обработчик
onconnect вызывается при каждом новом подключении клиента,
а сам воркер продолжает существовать независимо от жизненного цикла
конкретных страниц.
Parcel обрабатывает импорт воркеров как отдельный граф зависимостей. Поддерживается стандартный синтаксис создания воркера:
const worker = new SharedWorker(
new URL('./shared-worker.js', import.meta.url)
);
worker.port.start();
worker.port.onmess age = (event) => {
console.log('Сообщение от воркера:', event.data);
};
worker.port.postMessage({
type: 'ping',
});
Parcel анализирует new URL(..., import.meta.url) и
формирует корректный отдельный бандл для воркера, обеспечивая:
Parcel рассматривает Shared Worker как отдельную точку входа, аналогично Web Worker. При этом сохраняется связь с основным приложением через URL-резолвинг.
При сборке создаются:
Это обеспечивает:
Shared Worker в Parcel поддерживает ES Modules, что позволяет строить сложную архитектуру:
// shared-worker.js
import { formatMessage } from './utils/format.js';
const clients = new Set();
self.onconn ect = (event) => {
const port = event.ports[0];
clients.add(port);
port.start();
port.onmess age = (e) => {
const formatted = formatMessage(e.data);
for (const client of clients) {
client.postMessage(formatted);
}
};
};
Parcel автоматически:
Shared Worker имеет ряд архитектурных ограничений, которые влияют на проектирование при использовании Parcel:
Эти особенности требуют явного контроля жизненного цикла соединений:
self.onconn ect = (event) => {
const port = event.ports[0];
port.onmess age = () => {
// обработка сообщений
};
port.start();
};
Shared Worker часто используется как централизованное хранилище состояния между вкладками.
Пример синхронизации состояния:
const state = {
counter: 0,
};
const ports = new Set();
self.onconn ect = (event) => {
const port = event.ports[0];
ports.add(port);
port.start();
port.onmess age = (e) => {
if (e.data.type === 'increment') {
state.counter += 1;
for (const p of ports) {
p.postMessage({
type: 'state',
payload: state.counter,
});
}
}
};
port.postMessage({
type: 'state',
payload: state.counter,
});
};
Parcel не вмешивается в логику состояния, но обеспечивает стабильную упаковку кода и повторное использование воркера между сессиями.
Parcel поддерживает HMR и для Shared Worker, однако поведение отличается от основного приложения.
При обновлении модуля:
Типичный сценарий обработки обновления:
if (module.hot) {
module.hot.dispose(() => {
self.close();
});
}
Shared Worker не перезапускается “мягко” — пересборка приводит к новому экземпляру.
Клиентская часть отвечает за:
Shared Worker отвечает за:
Parcel обеспечивает корректное разделение этих слоёв на уровне сборки.
Shared Worker обладает особенностью повторного использования между вкладками, что снижает нагрузку на:
Parcel дополнительно усиливает эффект за счёт:
При изменении кода воркера меняется его URL, что приводит к созданию новой версии процесса.
Parcel поддерживает TypeScript в Shared Worker без дополнительной конфигурации:
interface Message {
type: string;
payload: unknown;
}
self.onconn ect = (event: MessageEvent) => {
const port = event.ports[0];
port.onmess age = (e: MessageEvent<Message>) => {
port.postMessage({
type: 'echo',
payload: e.data.payload,
});
};
port.start();
};
Типизация не влияет на runtime, но позволяет структурировать обмен сообщениями между вкладками.
Shared Worker занимает промежуточное положение между Service Worker и Dedicated Worker.
Shared Worker:
Service Worker:
Dedicated Worker:
Parcel поддерживает все три типа, но Shared Worker требует особой модели сборки из-за постоянного соединения между клиентами.
Ошибки в Shared Worker не всегда приводят к завершению процесса, поэтому требуется явная обработка:
self.oner ror = (error) => {
// логирование или отправка в мониторинг
};
Parcel не перехватывает runtime-ошибки внутри воркера, но обеспечивает корректное обновление бандла при изменении исходного кода.
Shared Worker часто применяется для:
Parcel в таких сценариях выступает как слой, обеспечивающий: