Влияние NavigationPreload на производительность

NavigationPreload — это функциональность Service Worker, которая позволяет одновременно загружать HTML-документ при активации навигации и выполнять установку Service Worker. Основная цель — снизить задержку отклика при первой загрузке страницы, когда Service Worker ещё не полностью активирован.

Механизм работы NavigationPreload

По умолчанию при навигации браузер сначала инициирует запрос к серверу, а Service Worker перехватывает его через событие fetch. Если Service Worker ещё не активен, запрос задерживается до его активации. Это создаёт дополнительную задержку.

NavigationPreload устраняет эту проблему за счёт параллельного запуска:

  1. Браузер инициирует запрос на сервер сразу после старта навигации.
  2. Service Worker активируется и может использовать уже выполненный предварительный запрос (preloadResponse).
  3. Если Service Worker готов, он может обработать ответ самостоятельно или вернуть предварительно загруженный результат.

Включение NavigationPreload

NavigationPreload активируется через объект registration.navigationPreload. Пример конфигурации:

self.addEventListener('activate', event => {
  event.waitUntil(
    (async () => {
      if ('navigationPreload' in self.registration) {
        await self.registration.navigationPreload.enable();
      }
    })()
  );
});

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

Использование preloadResponse

После включения NavigationPreload в обработчике события fetch можно получать предварительный ответ:

self.addEventListener('fetch', event => {
  if (event.request.mode === 'navigate') {
    event.respondWith((async () => {
      const preloadResponse = await event.preloadResponse;
      if (preloadResponse) {
        return preloadResponse;
      }
      return fetch(event.request);
    })());
  }
});

Здесь event.preloadResponse возвращает промис, который либо содержит предварительно загруженный ответ, либо undefined, если загрузка ещё не завершена.

Влияние на производительность

  1. Сокращение времени первого байта (TTFB) NavigationPreload позволяет начать загрузку HTML ещё до активации Service Worker, что уменьшает время ожидания отклика и снижает задержку визуального рендеринга страницы.

  2. Уменьшение блокировок при обновлении Service Worker При обновлении или повторной регистрации Service Worker без NavigationPreload браузер может ожидать завершения установки, что увеличивает задержку. NavigationPreload предотвращает простои, так как загрузка документа идёт параллельно.

  3. Совместимость с кэшированием и стратегиями Workbox NavigationPreload легко интегрируется с стратегиями Workbox (StaleWhileRevalidate, NetworkFirst). Например, в стратегии NetworkFirst предварительный ответ может быть использован как fallback при сетевых задержках:

workbox.routing.registerRoute(
  ({request}) => request.mode === 'navigate',
  async ({event}) => {
    try {
      const networkResponse = await fetch(event.request);
      return networkResponse;
    } catch (error) {
      const preloadResponse = await event.preloadResponse;
      if (preloadResponse) return preloadResponse;
      return caches.match('/offline.html');
    }
  }
);
  1. Снижение perceived latency Пользователь ощущает ускорение, так как основная HTML-разметка загружается быстрее, а Service Worker получает возможность кэшировать и оптимизировать ресурсы без задержки отображения страницы.

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

  • NavigationPreload работает только для навигационных запросов (mode: 'navigate').
  • Не заменяет кэширование: предварительный запрос ускоряет получение HTML, но ресурсы, такие как скрипты и стили, должны обрабатываться отдельно через стратегии кэширования Workbox.
  • Может потребовать корректной настройки заголовков Cache-Control на сервере, чтобы избежать повторного запроса или некорректного кэширования.

Советы по оптимизации

  • Всегда проверять if ('navigationPreload' in self.registration) перед активацией, чтобы избежать ошибок в старых браузерах.
  • Использовать NavigationPreload совместно с StaleWhileRevalidate для CSS и JS, чтобы минимизировать задержки визуального рендеринга.
  • Логировать event.preloadResponse в процессе разработки для понимания, когда предварительный ответ используется, а когда — нет.

NavigationPreload является мощным инструментом для снижения задержки при загрузке страниц и увеличения отзывчивости веб-приложений, особенно в сочетании с Workbox и современными стратегиями кэширования.