Prefetch и preload директивы

Современные сборщики модулей стремятся не только оптимизировать процесс сборки, но и управлять поведением загрузки кода в браузере. В Vite эти задачи решаются через тонкую интеграцию с динамическими импортами и автоматическую генерацию подсказок для браузера в виде prefetch и preload директив. Эти механизмы позволяют управлять приоритетами загрузки чанков, снижать время до интерактивности и перераспределять сетевые ресурсы более предсказуемым образом.


Базовая модель код-сплиттинга в Vite

Vite опирается на нативные ES-модули и динамический импорт:

const module = await import('./feature.js')

Каждый динамический импорт становится отдельным чанком. По умолчанию браузер загружает такие чанки только тогда, когда код действительно выполняется. Это создаёт ленивую модель загрузки, но не даёт возможности заранее подготовить ресурсы.

Для управления этим поведением Vite добавляет механизм подсказок загрузки через специальные атрибуты ссылок, которые внедряются в HTML или через runtime-обвязки.


Разделение preload и prefetch на уровне браузера

preload

preload обозначает ресурс, который необходим в ближайшем будущем и критичен для текущего рендеринга страницы.

Характеристики:

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

Пример HTML-механизма:

<link rel="preload" href="/assets/feature.js" as="script">

prefetch

prefetch используется для ресурсов, которые могут понадобиться в будущем, но не являются критичными для текущего состояния страницы.

Характеристики:

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

Пример:

<link rel="prefetch" href="/assets/feature.js" as="script">

Роль Vite в генерации preload и prefetch

Vite анализирует граф зависимостей и динамические импорты, после чего решает, какие чанки стоит предварительно загрузить. Основной механизм работает через Rollup-плагин @vitejs/plugin-vue или встроенную оптимизацию HTML.

Автоматический preload для критических чанков

Vite автоматически добавляет preload для модулей, которые:

  • импортируются синхронно в entry-point;
  • участвуют в начальном рендере;
  • необходимы для гидратации (в SSR-режимах);
  • входят в основной граф зависимостей приложения.

Пример поведения:

import Header from './components/Header.vue'

Такой модуль попадёт в основной бандл или будет помечен как критический чанк, если происходит код-сплиттинг.


Prefetch для динамических импортов

Динамические импорты:

const openModal = async () => {
  const module = await import('./Modal.vue')
}

Vite рассматривает их как кандидатов для prefetch, так как они:

  • не требуются при первом рендере;
  • могут быть востребованы при взаимодействии пользователя;
  • часто связаны с UI-событиями.

При сборке Vite может генерировать:

<link rel="prefetch" href="/assets/Modal.js">

Интеграция с Rollup графом модулей

В основе Vite лежит Rollup, который строит статический граф зависимостей. Каждый узел графа имеет метаданные:

  • entry points;
  • static imports;
  • dynamic imports;
  • shared chunks.

На основе этих данных формируется стратегия загрузки:

  1. Entry chunk получает preload.
  2. Асинхронные чанки получают prefetch.
  3. Общие зависимости могут получать оба типа директив в зависимости от контекста.

HTML-инъекция директив

Vite внедряет preload/prefetch через HTML-transform плагин. При обработке index.html выполняется анализ тегов <script type="module">.

Пример исходного HTML:

<script type="module" src="/src/main.js"></script>

После обработки Vite может добавить:

<link rel="modulepreload" href="/assets/vendor.js">
<link rel="modulepreload" href="/assets/main.js">

modulepreload является более специализированной формой preload, предназначенной для ES-модулей.


modulepreload как расширение preload

В контексте Vite чаще используется именно modulepreload.

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

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

Пример:

<link rel="modulepreload" href="/assets/app.js">

Поведение при динамических маршрутах

В приложениях с роутингом (например, Vue Router или React Router) Vite не знает заранее, какой маршрут будет активен. Поэтому:

  • основной маршрут получает preload;
  • маршруты, скрытые за lazy-loading, получают prefetch;
  • связанные чанки могут группироваться в отдельные бандлы.

Пример:

const Home = () => import('./pages/Home.vue')
const About = () => import('./pages/About.vue')

Результат сборки обычно включает:

  • основной chunk для приложения;
  • отдельные chunks для страниц;
  • prefetch-директивы для неактивных страниц.

Приоритизация загрузки

Браузерная модель приоритизации взаимодействует с директивами следующим образом:

  1. HTML и критический CSS — максимальный приоритет.
  2. preload / modulepreload — высокий приоритет.
  3. обычные script fetch — средний приоритет.
  4. prefetch — низкий приоритет.

Vite использует эту модель для балансировки:

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

Конфликты и дублирование загрузок

Некорректная конфигурация может приводить к повторной загрузке одного и того же модуля:

  • preload + dynamic import одного чанка;
  • prefetch + переход до завершения загрузки;
  • пересечение shared chunks.

Современные браузеры частично оптимизируют это через deduplication, но Vite старается минимизировать такие ситуации на этапе сборки.


Влияние на кеширование

prefetch и preload не только влияют на приоритет загрузки, но и на поведение кеша:

  • ресурсы загружаются заранее и попадают в HTTP cache;
  • subsequent navigation использует уже загруженные чанки;
  • уменьшается latency при переходах между страницами.

Особенно заметен эффект в SPA с большим количеством ленивых маршрутов.


SSR и hydration

В SSR-сценариях Vite дополнительно синхронизирует:

  • серверный рендер HTML;
  • список критических модулей;
  • клиентские preload директивы.

Это позволяет:

  • минимизировать waterfall загрузок;
  • ускорить hydration;
  • избежать двойной загрузки модулей.

Ограничения и особенности поведения

  • prefetch не гарантирует загрузку ресурса;
  • preload может перегружать сеть при избыточном использовании;
  • modulepreload не поддерживается старыми браузерами;
  • динамические импорты с переменными путями ограничивают анализ графа;
  • поведение может отличаться в dev и build режимах.

Практическая модель принятия решений внутри Vite

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

  • если модуль участвует в первом рендере → preload;
  • если модуль связан с будущими действиями → prefetch;
  • если модуль общий для нескольких чанков → modulepreload;
  • если модуль динамический и изолированный → lazy import без подсказок.

Связь с оптимизацией производительности

Использование preload и prefetch в Vite напрямую влияет на ключевые метрики:

  • FCP (First Contentful Paint) — через ускорение критических зависимостей;
  • TTI (Time to Interactive) — через параллельную загрузку модулей;
  • LCP (Largest Contentful Paint) — через ускорение ресурсов рендеринга;
  • INP — через сокращение задержек при взаимодействии с ленивыми компонентами.

Модель загрузки становится предсказуемой за счёт разделения критических и некритических чанков на уровне сборки, а не runtime.