Таргет для Web Workers

Web Workers в экосистеме JavaScript представляют собой механизм выполнения кода в отдельном потоке, изолированном от основного потока браузера. В контексте сборщика Parcel поддержка Workers реализована как часть системы таргетов (targets), позволяющей формировать отдельные бандлы с собственными правилами компиляции, окружением и точками входа.

Parcel автоматически распознаёт использование Worker-кода и выделяет его в отдельную сборку, обеспечивая корректную работу изолированного контекста выполнения без дополнительной ручной конфигурации в большинстве сценариев.


Архитектура Web Worker в Parcel

Parcel рассматривает Web Worker как самостоятельную единицу сборки. При обнаружении создания воркера формируется отдельный граф зависимостей, который изолируется от основного приложения.

Ключевые особенности архитектуры:

  • каждый Worker компилируется в отдельный бандл;
  • зависимости Worker не смешиваются с main bundle;
  • код Worker обрабатывается через собственный runtime;
  • применяется отдельный target environment;
  • обеспечивается независимая оптимизация и минификация.

Такое разделение позволяет Parcel выполнять tree-shaking и code-splitting отдельно для главного потока и фоновых потоков.


Способы создания Web Worker в Parcel

Parcel поддерживает несколько стандартных способов подключения Worker-кода.

1. Использование конструктора Worker с URL

Наиболее распространённый вариант:

const worker = new Worker(
  new URL('./worker.js', import.meta.url),
  { type: 'module' }
);

Parcel анализирует new URL(..., import.meta.url) и автоматически:

  • выделяет worker.js в отдельный бандл;
  • настраивает корректный путь к сгенерированному файлу;
  • применяет отдельный target для Worker окружения.

2. Использование прямого импорта Worker

Parcel также поддерживает более декларативный синтаксис:

import Worker from './worker.js';

const worker = new Worker();

При такой записи Parcel интерпретирует модуль как Worker entrypoint и создаёт отдельную сборку без необходимости ручного указания URL.


3. Worker через query-суффикс

В некоторых конфигурациях используется явное указание:

const worker = new Worker('./worker.js?worker', { type: 'module' });

Parcel распознаёт ?worker как инструкцию для создания отдельного worker-bundle.


Target environment для Web Workers

Parcel использует систему targets, определяемую в package.json. Для Web Worker применяется отдельный runtime environment.

Пример конфигурации:

{
  "targets": {
    "default": {
      "context": "browser"
    },
    "worker": {
      "context": "web-worker"
    }
  }
}

Контекст web-worker

Контекст web-worker определяет:

  • отсутствие доступа к DOM;
  • наличие глобального объекта self вместо window;
  • поддержку стандартных Web Worker API;
  • ограниченный набор браузерных возможностей;
  • отдельный scope исполнения.

Parcel использует этот контекст для корректной трансформации кода, исключая несовместимые полифилы и оптимизации.


Изоляция зависимостей Worker

Worker-бандл в Parcel строится как независимый граф модулей. Это означает:

  • импортируемые модули внутри Worker не попадают в main bundle;
  • общие зависимости могут дублироваться или шариться через оптимизации;
  • код Worker не влияет на размер основного бандла напрямую;
  • tree-shaking применяется отдельно.

Пример структуры:

index.js  → main bundle
worker.js → worker bundle
utils.js  → может попасть в оба бандла при раздельном использовании

Особенности трансформации кода

Parcel применяет отдельные пайплайны трансформации для Worker-кода:

  • Babel/TypeScript компиляция выполняется независимо;
  • минификация производится отдельно;
  • scope-hoisting оптимизируется внутри worker-графа;
  • HMR (Hot Module Replacement) может работать ограниченно в зависимости от среды.

Важно, что Worker код рассматривается как самостоятельное приложение внутри сборки.


Передача данных между main thread и Worker

Parcel не изменяет стандартное поведение Web Workers, но обеспечивает корректную упаковку сообщений:

// main.js
const worker = new Worker(
  new URL('./worker.js', import.meta.url),
  { type: 'module' }
);

worker.postMessage({ value: 42 });

worker.onmess age = (event) => {
  console.log(event.data);
};
// worker.js
self.onmess age = (event) => {
  const result = event.data.value * 2;
  self.postMessage(result);
};

Parcel гарантирует, что:

  • сериализация сообщений не нарушается;
  • worker bundle корректно загружается в runtime;
  • пути к ресурсам внутри worker корректно резолвятся.

Ленивая загрузка Worker

Worker автоматически становится кандидатом на lazy-loading, так как выделяется в отдельный файл.

При использовании динамического импорта:

const worker = new Worker(
  new URL('./heavyWorker.js', import.meta.url),
  { type: 'module' }
);

поведение включает:

  • загрузку worker-бандла только при создании экземпляра;
  • отсутствие блокировки основного бандла;
  • возможность кеширования отдельного файла.

Работа с ресурсами внутри Worker

Worker в Parcel может импортировать:

  • JS/TS модули;
  • JSON;
  • изображения (как URL);
  • WASM модули;
  • CSS (ограниченно, как side-effect или обработанный модуль).

Пример:

import config from './config.json';
import imageUrl from './image.png';

self.postMessage({ config, imageUrl });

Parcel трансформирует ресурсы в валидные URL внутри worker bundle.


TypeScript и Web Workers

При использовании TypeScript Worker код обрабатывается отдельным компиляторным пайплайном:

self.onmess age = (event: MessageEvent<number>) => {
  self.postMessage(event.data * 2);
};

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

  • типы изолированы от main thread;
  • отдельные tsconfig могут применяться через targets;
  • отсутствует пересечение DOM типов при web-worker контексте.

Оптимизация Worker-бандлов

Parcel применяет специфические оптимизации:

Tree Shaking

Удаляются неиспользуемые экспорты внутри worker-графа.

Scope Hoisting

Модули объединяются в минимальное количество функций исполнения.

Code Splitting

Если worker импортирует динамические модули, Parcel формирует дополнительные чанки.

Minification

Worker-бандлы минифицируются независимо от основного приложения.


Ошибки конфигурации и поведение Parcel

При неправильной настройке Worker могут возникать типовые сценарии:

  • попытка доступа к DOM внутри worker-кода;
  • импорт node-specific модулей без polyfill;
  • некорректный путь к worker без import.meta.url;
  • отсутствие type: "module" при ESModule Worker.

Parcel в таких случаях:

  • сообщает об ошибке сборки;
  • либо корректно отделяет окружение при обнаружении target;
  • либо генерирует несовместимый runtime warning.

Разделение targets для сложных приложений

В крупных проектах Worker target часто комбинируется с другими окружениями:

{
  "targets": {
    "main": {
      "context": "browser",
      "engines": {
        "browsers": "> 0.25%"
      }
    },
    "worker": {
      "context": "web-worker"
    }
  }
}

Такой подход позволяет:

  • оптимизировать main bundle под браузеры;
  • отдельно минимизировать worker runtime;
  • избегать лишних полифилов внутри worker.

Взаимодействие Worker с другими Parcel-фичами

Web Workers в Parcel тесно интегрированы с:

  • Hot Module Replacement — ограниченная поддержка обновления worker-кода;
  • Asset Pipeline — единая система обработки ресурсов;
  • Caching System — раздельное кеширование worker и main bundles;
  • Dev Server — автоматическая пересборка worker при изменениях.

Parcel отслеживает зависимости worker-графа так же, как и основного приложения, но с изоляцией исполнения.


Статическая и динамическая модель Worker

Parcel различает два подхода:

Статический Worker

import Worker from './worker.js';
const w = new Worker();
  • фиксированный entrypoint;
  • заранее известный граф;
  • оптимизация на этапе сборки.

Динамический Worker

const w = new Worker(
  new URL(`./workers/${name}.js`, import.meta.url),
  { type: 'module' }
);
  • runtime-определяемый путь;
  • ограниченная статическая аналитика;
  • возможное увеличение количества чанков.

Модель исполнения в продакшене

В production режиме Parcel:

  • генерирует отдельные worker bundle файлы;
  • заменяет исходные пути на хэшированные;
  • оптимизирует загрузку через кеширование браузера;
  • минимизирует пересечения зависимостей между потоками.

Worker становится полностью независимым артефактом сборки, подключаемым через URL, с сохранением всех трансформаций, применённых в процессе сборки.