Code splitting в esbuild предназначен для разбиения итогового бандла на несколько независимых файлов (чанков), которые загружаются по мере необходимости. Основная цель — уменьшение начального размера загрузки приложения и оптимизация времени старта, особенно в SPA и крупных фронтенд-системах.
Механизм активируется через опцию:
splitting: trueи работает только в связке с модулем ECMAScript:
format: "esm"Это ограничение связано с тем, что динамическая загрузка модулей и статический граф зависимостей реализуются нативно именно в ESM.
Для корректного включения разбиения кода необходимо соблюдение нескольких условий:
esmimport()splitting: truebundle: trueПример базовой конфигурации:
import * as esbuild from "esbuild";
esbuild.build({
entryPoints: ["src/app.js"],
bundle: true,
splitting: true,
format: "esm",
outdir: "dist",
target: "es2020"
});
При несоблюдении любого из этих условий esbuild либо игнорирует splitting, либо выдаёт ошибку конфигурации.
Основной триггер разбиения кода — использование
import():
// src/app.js
button.addEventListener("click", async () => {
const module = await import("./heavy-module.js");
module.runHeavyTask();
});
При сборке esbuild выделяет heavy-module.js в отдельный
файл чанка, который загружается только при вызове события.
Это позволяет:
esbuild строит граф зависимостей и анализирует:
import ... from)import())Статические зависимости попадают в основной граф, а динамические — формируют точки разделения.
Пример структуры:
app.js
├── utils.js
├── vendor.js
└── lazy.js (dynamic import)
Результат сборки:
dist/
├── app.js
├── utils.js
├── vendor.js
└── lazy-[hash].js
Если несколько чанков используют один и тот же модуль, esbuild автоматически выделяет его в общий shared chunk.
Пример:
// a.js
import { format } from "./utils";
// b.js
import { format } from "./utils";
При динамическом импорте обоих модулей:
import("./a.js");
import("./b.js");
utils.js будет вынесен в отдельный общий чанк.
Это поведение предотвращает дублирование кода и уменьшает общий размер загрузки.
Несмотря на высокую скорость сборки, механизм имеет ряд ограничений:
CommonJS не поддерживает разделение кода:
format: "cjs" → splitting недоступенesbuild не предоставляет собственного загрузчика чанков. Браузер выполняет загрузку через стандартный ESM loader.
В отличие от Webpack, отсутствуют:
Code splitting тесно взаимодействует с tree shaking. При включении:
Это приводит к более точному распределению кода между файлами.
export function used() {}
export function unused() {}
Если unused не импортируется, он не попадёт ни в один
чанк.
Использование splitting формирует архитектурные границы приложения. Типичные сценарии:
Пример:
router.on("/dashboard", async () => {
const dashboard = await import("./pages/dashboard.js");
dashboard.render();
});
Каждый маршрут становится отдельной точкой загрузки.
По умолчанию esbuild генерирует имена файлов автоматически. Однако можно управлять этим через:
entryNameschunkNamesassetNamesПример:
esbuild.build({
entryPoints: ["src/app.js"],
bundle: true,
splitting: true,
format: "esm",
outdir: "dist",
chunkNames: "chunks/[name]-[hash]"
});
Результат:
dist/chunks/utils-a1b2c3.js
Использование [hash] важно для кеширования в
браузере.
esbuild позволяет получить структуру чанков через
metafile:
esbuild.build({
entryPoints: ["src/app.js"],
bundle: true,
splitting: true,
format: "esm",
outdir: "dist",
metafile: true
}).then(result => {
require("fs").writeFileSync(
"meta.json",
JSON.stringify(result.metafile, null, 2)
);
});
В metafile содержится:
Это используется для анализа бандла и оптимизации архитектуры.
Включение splitting влияет на:
В dev-режиме splitting сохраняется, но:
В production:
minify: true,
splitting: true,
format: "esm"
достигается максимальная оптимизация бандла.
При использовании npm-библиотек esbuild может:
Пример:
import lodash from "lodash";
import("./chart.js");
lodash может попасть в общий vendor chunk при наличии
нескольких точек использования.
Типичная структура после сборки:
dist/
├── app.js (entry)
├── vendor.js (общие зависимости)
├── route-home.js (lazy route)
├── route-admin.js (lazy route)
└── components-xyz.js (shared chunk)
Такое разбиение обеспечивает:
Опция target влияет на генерацию чанков:
Пример:
target: "es2022"
уменьшает объем runtime-обвязки и улучшает эффективность splitting.
Разделение кода усиливает эффективность HTTP-кеширования:
Использование хеширования имени чанков: