Рантайм Webpack: bootstrap-код и его назначение

После завершения сборки Webpack формирует не только набор модулей приложения, но и специальный служебный слой — рантайм. Этот код отвечает за запуск, связывание и выполнение модулей в браузере или другой среде выполнения. Он представляет собой bootstrap-обвязку, без которой сгенерированный бандл не способен корректно интерпретировать систему модулей.

Рантайм Webpack выполняет роль минимальной виртуальной модульной системы, реализующей механику загрузки CommonJS-подобных модулей, управление зависимостями и ленивую подгрузку чанков.


Общая структура bootstrap-кода

Сгенерированный Webpack бандл обычно представляет собой IIFE (Immediately Invoked Function Expression), внутри которой располагаются:

  • объект модулей
  • кеш загруженных модулей
  • функция require-совместимого импорта
  • система загрузки чанков
  • служебные утилиты рантайма

Упрощённая форма выглядит следующим образом:

(() => {
  const modules = {
    "./src/index.js": (module, exports, __webpack_require__) => {
      const dep = __webpack_require__("./src/util.js");
      console.log(dep);
    },

    "./src/util.js": (module, exports) => {
      module.exports = "util result";
    }
  };

  const cache = {};

  function __webpack_require__(id) {
    if (cache[id]) return cache[id].exports;

    const module = cache[id] = {
      exports: {}
    };

    modules[id](module, module.exports, __webpack_require__);

    return module.exports;
  }

  __webpack_require__("./src/index.js");
})();

Этот пример отражает базовую идею: Webpack превращает модули в функции, а рантайм обеспечивает их исполнение и кеширование.


Функция webpack_require как ядро рантайма

Центральным элементом bootstrap-кода является функция __webpack_require__. Она заменяет стандартную систему модулей и выполняет несколько ключевых задач:

Кеширование модулей

Каждый модуль выполняется только один раз. Результат сохраняется в кеше:

const cache = {};

if (cache[id]) return cache[id].exports;

Это поведение полностью повторяет семантику ES Modules и CommonJS, где повторный импорт не приводит к повторному выполнению кода.

Инициализация модуля

При первом обращении создаётся объект:

const module = {
  exports: {}
};

После выполнения функции модуля этот объект наполняется экспортами.

Инъекция зависимости

Каждый модуль получает доступ к __webpack_require__, что позволяет рекурсивно загружать зависимости:

modules[id](module, module.exports, __webpack_require__);

Система модулей внутри Webpack

Webpack компилирует все файлы в единый объект, где ключами выступают идентификаторы модулей (путь, хеш или числовой id), а значениями — функции-обёртки.

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

  • каждый модуль изолирован в своей функции
  • переменные не попадают в глобальную область видимости
  • зависимости разрешаются через __webpack_require__

Так достигается изоляция и предсказуемость выполнения кода.


Модульный кеш и предотвращение повторного выполнения

Кеш модулей играет критическую роль в производительности и корректности выполнения.

const cache = {};

function __webpack_require__(id) {
  if (cache[id]) {
    return cache[id].exports;
  }

  const module = cache[id] = { exports: {} };

  modules[id](module, module.exports, __webpack_require__);

  return module.exports;
}

Это обеспечивает:

  • единоразовую инициализацию модулей
  • сохранение состояния между импортами
  • защиту от циклических зависимостей (частично)

Поддержка циклических зависимостей

Webpack runtime способен обрабатывать циклические импорты за счёт раннего создания объекта module.exports.

При цикле:

  1. модуль создаётся и помещается в кеш
  2. начинается его выполнение
  3. при обращении к зависимому модулю возвращается частично инициализированный exports

Это поведение совпадает с моделью CommonJS.


Ленивые чанки и динамическая загрузка

Одной из ключевых функций рантайма является поддержка code splitting.

При использовании import() Webpack создаёт отдельные чанки и добавляет механизм их загрузки.

Упрощённая логика выглядит так:

__webpack_require__.e = function(chunkId) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");

    script.src = chunkId + ".js";

    script.onl oad = () => resolve();
    script.oner ror = () => reject();

    document.head.appendChild(script);
  });
};

Загрузка чанка включает:

  • создание <script> тега
  • загрузку отдельного JS-файла
  • регистрацию новых модулей в общем реестре
  • продолжение выполнения зависимого кода

JSONP-механизм загрузки чанков

Webpack часто использует JSONP-подход для интеграции новых модулей в рантайм.

Каждый чанк при загрузке вызывает глобальный callback:

webpackJsonpCallback([chunkIds, modules]);

Рантайм:

  • принимает новые модули
  • добавляет их в общий registry
  • резолвит промисы ожидания чанков

Это позволяет асинхронно расширять модульную систему без перезагрузки страницы.


Структура runtime-таблиц

Внутри рантайма присутствуют несколько ключевых структур:

Таблица модулей

var modules = {};

Хранит все синхронно доступные модули.

Кеш

var cache = {};

Хранит уже выполненные модули.

Таблица загружаемых чанков

var installedChunks = {
  main: 0
};

Используется для отслеживания состояния загрузки:

  • 0 — загружен
  • Promise — загружается

Интероперабельность с ES Modules

Webpack runtime поддерживает совместимость с ES Modules через специальные helpers:

  • __webpack_require__.r — пометка модуля как ESModule
  • __webpack_require__.d — определение getter-экспортов
  • __webpack_require__.n — обработка default-экспорта

Пример:

__webpack_require__.r(exports);

__webpack_require__.d(exports, {
  value: () => value
});

Это позволяет объединять CommonJS и ES Modules в одном графе зависимостей.


Ленивая инициализация и execution order

Рантайм Webpack строго контролирует порядок выполнения:

  • entry-модуль запускается последним
  • зависимости выполняются по мере необходимости
  • динамические импорты откладывают выполнение до загрузки чанка

Это обеспечивает детерминированный порядок инициализации.


Минификация runtime и production-оптимизации

В production-сборках рантайм подвергается агрессивной оптимизации:

  • сокращение имён (__webpack_require____webpack_require__ или ещё короче)
  • инлайнинг простых функций
  • удаление dev-хуков
  • слияние таблиц модулей

Это уменьшает размер bootstrap-кода до минимально возможного.


Роль runtime в архитектуре Webpack

Рантайм является связующим слоем между:

  • результатом компиляции (модули и чанки)
  • средой выполнения (браузер или Node.js)
  • системой загрузки и исполнения

Без него Webpack-выход представлял бы собой набор изолированных функций без механизма взаимодействия.

Фактически рантайм превращает набор файлов в работающую модульную систему, реализующую:

  • изоляцию кода
  • кеширование
  • динамическую загрузку
  • совместимость модульных стандартов