SystemJS представляет собой динамический загрузчик модулей, ориентированный на выполнение JavaScript в средах, где требуется универсальная поддержка форматов модулей: ESM, CommonJS, AMD и пользовательских форматов через плагины. Его ключевая идея заключается в предоставлении слоя абстракции над механизмом импорта, позволяющего исполнять модули независимо от их исходного синтаксиса и способа сборки.
В экосистеме современных инструментов SystemJS занимает промежуточное положение между нативными ES-модулями браузера и сборщиками вроде Webpack или Rollup. Он часто используется там, где требуется динамическая подгрузка кода, микрофронтенды или отложенная компиляция модулей на клиенте.
В основе SystemJS лежит граф зависимостей, который строится во время выполнения. Каждый модуль рассматривается как узел графа, а зависимости — как направленные ребра.
Основные особенности архитектуры:
SystemJS не требует предварительной полной сборки графа, что отличает его от классических бандлеров. Вместо этого он постепенно формирует граф по мере выполнения приложения.
Резолвинг в SystemJS происходит в несколько этапов:
Конфигурация map играет ключевую роль, позволяя переопределять пути зависимостей без изменения исходного кода:
System.config({
map: {
"lodash": "/vendor/lodash.js",
"app/": "/src/app/"
}
});
Такой подход делает возможной изоляцию логики приложения от физической структуры файловой системы.
SystemJS поддерживает несколько стратегий интерпретации модулей:
cjs
Формат System.register является ключевым для экосистемы, поскольку он обеспечивает предсказуемую загрузку зависимостей и совместимость с динамическим исполнением.
Пример модуля System.register:
System.register([&
return {
setters: [function (dep_1) {}],
execute: function () {
exports_1("default", "value");
}
};
});
SWC выступает как высокопроизводительный транспилятор, который может использоваться для подготовки кода к исполнению в SystemJS. Его основная задача в таком контексте — преобразование современного JavaScript (ESNext, TypeScript) в формат, совместимый с выбранной стратегией загрузки.
В связке SWC и SystemJS обычно реализуется следующий пайплайн:
Пример конфигурации SWC для генерации SystemJS-совместимого кода:
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2020"
},
"module": {
"type": "systemjs"
}
}
В этом режиме SWC берёт на себя роль компилятора модулей, формируя структуру, которую SystemJS способен интерпретировать без дополнительной трансформации в браузере.
SystemJS позволяет реализовывать ленивую загрузку модулей без предварительной сборки всего приложения в единый бандл. Модули загружаются по мере необходимости через стандартный вызов import-подобного API:
System.import('/modules/dashboard.js')
.then(module => {
module.init();
});
Особенность подхода заключается в том, что загрузчик самостоятельно управляет кэшированием и порядком выполнения зависимостей.
Код-сплиттинг в такой архитектуре не требует статического анализа всего приложения. Разбиение происходит на уровне файловой структуры или логических маршрутов.
Import maps позволяют задавать правила резолвинга на уровне браузера, а SystemJS может использовать их как дополнительный слой конфигурации.
Пример import map:
<script type="importmap">
{
"imports": {
"react": "/cdn/react.js",
"app/": "/src/app/"
}
}
</script>
SystemJS учитывает эти правила при разрешении зависимостей, что снижает необходимость в ручной настройке map-конфигурации внутри загрузчика.
Import maps особенно важны в микрофронтенд-архитектурах, где разные части приложения могут поставляться независимо.
Одной из ключевых задач SystemJS является унификация несовместимых модульных систем. CommonJS модули преобразуются в асинхронные обёртки, что позволяет им существовать в ESM-графе.
Основные механизмы адаптации:
require в асинхронный резолвер;
module.exports;
При этом поведение может отличаться от нативного Node.js, особенно в части циклических зависимостей и кеширования.
SystemJS поддерживает расширение поведения через плагины загрузки. Плагин представляет собой модуль, который обрабатывает определённый тип ресурсов.
Типичный сценарий — загрузка JSON:
System.import('./data.json!json').then(data => {
console.log(data);
});
Здесь !json указывает на необходимость применения
соответствующего трансформера.
Плагинная система позволяет:
Во время выполнения SystemJS строит ориентированный ациклический граф зависимостей, где порядок исполнения определяется топологической сортировкой.
Процесс включает:
Такая модель обеспечивает детерминированный порядок и предсказуемость инициализации.
Использование SystemJS в runtime накладывает определённые ограничения:
В сочетании со SWC часть этих проблем компенсируется предварительной трансформацией кода, уменьшающей нагрузку на клиентскую среду.
SystemJS часто применяется в архитектуре микрофронтендов, где разные части интерфейса загружаются независимо и могут иметь различные циклы деплоя.
Типичная структура:
Такой подход позволяет разделять ответственность между командами и минимизировать связность кода.
В гибридных системах SWC используется для предварительной компиляции критических модулей, тогда как SystemJS загружает и интерпретирует второстепенные части приложения динамически.
Это создаёт двухуровневую модель:
Подобная архитектура позволяет балансировать между скоростью загрузки и гибкостью обновлений кода без полной пересборки приложения.