Генерация манифеста через build.manifest

## Механизм генерации manifest в сборке Vite В процессе production-сборки Vite формирует оптимизированный набор статических ресурсов, разбивая код на чанки, применяя хеширование имён файлов и выполняя tree-shaking. В некоторых архитектурах приложений требуется явное знание того, какие файлы были сгенерированы сборщиком, их взаимосвязей и точек входа. Для этих целей используется режим генерации манифеста сборки. ## Назначение build.manifest Параметр `build.manifest` включает генерацию JSON-файла, содержащего карту всех ассетов, созданных во время сборки. Этот файл выступает как индекс артефактов сборки и позволяет внешним системам или серверной части приложения точно определять: * какие JS и CSS файлы соответствуют конкретным entry points * какие чанки являются динамическими зависимостями * какие ассеты были извлечены из модулей * какие файлы имеют хешированные имена Основная идея заключается в переносе ответственности за связывание HTML и ассетов с клиента на сервер или отдельный слой интеграции. ## Включение manifest в конфигурации Включение механизма выполняется через конфигурационный файл Vite: ```javascript import { defineConfig } from 'vite' export default defineConfig({ build: { manifest: true } }) ``` После выполнения сборки в директории `dist` появляется файл `manifest.json`. ## Структура manifest.json Содержимое манифеста представляет собой объект, где ключами являются исходные входные модули или виртуальные entry points, а значениями — описания соответствующих собранных ресурсов. Пример упрощённой структуры: ```json { "src/main.js": { "file": "assets/main.8f3a1c2d.js", "src": "src/main.js", "isEntry": true, "css": ["assets/main.4e2b1c9d.css"], "imports": ["vendor.js"] }, "vendor.js": { "file": "assets/vendor.91ab3f0c.js", "isEntry": false } } ``` Каждое поле имеет конкретную семантику: * `file` — основной JS-файл чанка * `css` — массив CSS-файлов, извлечённых из данного entry * `imports` — зависимости, подгружаемые динамически или как shared chunks * `isEntry` — признак точки входа ## Роль manifest в архитектуре SSR В серверно-рендеринговых приложениях (SSR) манифест используется как связующее звено между сервером и клиентскими ассетами. Сервер не знает заранее имён файлов, поскольку Vite добавляет хеши для кеширования. Поэтому именно manifest становится источником истины. Типичный сценарий: 1. Сервер обрабатывает запрос 2. Определяет нужный entry point 3. Считывает manifest.json 4. Находит соответствующие JS и CSS файлы 5. Вставляет их в HTML-ответ Пример серверной логики: ```javascript import manifest from './dist/manifest.json' assert { type: 'json' } function getAssets(entry) { const chunk = manifest[entry] return { js: chunk.file, css: chunk.css || [] } } ``` ## Связь manifest и code splitting Vite автоматически применяет разделение кода при использовании динамических импортов: ```javascript import('./module.js') ``` Каждый такой импорт формирует отдельный chunk, который также попадает в manifest. В результате manifest становится графом зависимостей, отражающим структуру приложения после оптимизации. Особенности отражения динамических импортов: * каждый async chunk получает собственный ключ * зависимости между чанками фиксируются через `imports` * повторно используемые модули выносятся в shared chunks ## Использование manifest в шаблонизации HTML В классическом SPA Vite сам подставляет скрипты в HTML. Однако при серверной интеграции HTML формируется вручную. Пример генерации HTML: ```javascript function renderHtml(entry) { const { js, css } = getAssets(entry) const cssLinks = css .map(file => `
  • `) .join('\n') return ` ${cssLinks}
    ` } ``` Такой подход обеспечивает полный контроль над вставкой ассетов. ## Manifest и многопоточность сборки При использовании нескольких entry points Vite формирует единый manifest, содержащий все точки входа. Это важно для монорепозиториев или мультистраничных приложений. Пример конфигурации: ```javascript export default defineConfig({ build: { manifest: true, rollupOptions: { input: { main: 'index.html', admin: 'admin.html' } } } }) ``` В результате manifest будет содержать записи для каждой страницы отдельно. ## Отличие manifest от assets manifest и ssr manifest Vite может формировать несколько типов манифестов: ### build.manifest Обычный клиентский манифест, ориентированный на mapping entry → assets. ### build.ssrManifest Используется для серверного рендера и hydration. Включается отдельно: ```javascript export default defineConfig({ build: { ssrManifest: true } }) ``` Этот файл содержит информацию о модулях, необходимых для корректной гидратации на клиенте. ## Оптимизация кеширования через manifest Manifest тесно связан с системой хеширования Vite. Каждый файл получает уникальный hash на основе содержимого, что позволяет: * безопасно кэшировать ассеты в CDN * обновлять только изменённые чанки * избегать cache invalidation на уровне HTML Пример: ``` main.js → main.8f3a1c2d.js main.js (изменён) → main.91bd77aa.js ``` Manifest автоматически отражает эти изменения без необходимости изменения серверной логики. ## Интеграция с backend-фреймворками Manifest часто используется в связке с серверными фреймворками: * Node.js (Express, Fastify) * PHP (Laravel, Symfony) * Go-based backend * SSR-фреймворки на Vite Пример использования в PHP-архитектуре: ```php $manifest = json_decode(file_get_contents('dist/manifest.json'), true); $entry = $manifest['src/main.js']; echo ''; ``` ## Поведение при инкрементальной сборке При каждой новой production-сборке manifest полностью пересоздаётся. Он не является инкрементальным кэшем, а представляет актуальное состояние графа зависимостей. Это означает: * старые ключи удаляются * новые чанки добавляются * структура может изменяться даже при небольших правках кода ## Ограничения и особенности Manifest не предназначен для: * хранения runtime-состояния * динамического изменения в браузере * частичного обновления без пересборки Его роль строго ограничена описанием статических артефактов сборки. Также важно учитывать, что: * структура может отличаться между версиями Vite * плагины Rollup могут расширять содержимое manifest * порядок ключей не гарантирует семантической значимости ## Взаимодействие с плагинами Плагины Vite и Rollup могут модифицировать выходной manifest. Это используется для: * добавления метаданных * группировки ассетов * внедрения SSR-хинтов * интеграции с backend-системами Пример плагина: ```javascript export default function manifestPlugin() { return { name: 'manifest-plugin', generateBundle(_, bundle) { // модификация структуры output } } } ``` ## Роль manifest в сложных SPA архитектурах В крупных приложениях manifest становится центральным элементом связывания слоёв: * frontend сборка * backend рендеринг * CDN кеширование * edge rendering Он обеспечивает единый контракт между сборкой и исполнением приложения, исключая необходимость ручного отслеживания файлов.