НейроАгент

Исправление двойных слэшей Workbox в Vite PWA на GitHub Pages

Решение проблемы двойных слэшей в ключах кэша Workbox при развертывании Vite PWA на GitHub Pages. Узнайте о исправлениях конфигурации и лучших практиках для правильной нормализации URL.

Настройка Workbox в Vite PWA (размещенном на GitHub Pages) приводит к двойным слэшам (//) в записях кэша?

Я столкнулся с проблемой, когда мой кэш Workbox заполняется ключами с двойными слэшами. Это происходит, когда мой Vite PWA размещен на GitHub Pages.

Я уже пробовал:

  • Настройку базовой конфигурации в Vite (с и без завершающих слэшей)
  • Очистку данных сайта и отмену регистрации сервис-воркеров во время тестирования

Сгенерированный сервис-воркер не включает суффикс URL из GitHub Actions, что указывает на то, что это может быть внутреннее поведение Workbox.

Мой workflow GitHub Actions включает:

yaml
- name: Production Build
  run: bun run build:prod -- --base=/${{ github.event.repository.name }}/

Я создал воспроизводимый пример с артефактами развертываний GitHub, хотя проблема проявляется конкретно при размещении на GitHub Pages или при обслуживании не из корневой папки сервера.

Какие параметры конфигурации следует изучить или какие альтернативные методы следует рассмотреть для обеспечения правильных ключей кэша без двойных слэшей?

НейроАгент

Workbox в Vite PWA может генерировать двойные слэши в ключах кэша при размещении на GitHub Pages из-за обработки базового URL во время регистрации сервис-воркера и генерации ключей кэша. Это обычно происходит потому, что скрипт сервис-воркера загружается с другим базовым путем, чем фактические записи кэша, которые он пытается предварительно кэшировать или перехватить, что приводит к неправильной нормализации URL в Workbox.

Содержание

Понимание проблемы двойных слэшей

Проблема двойных слэшей (//) в ключах кэша Workbox проявляется, когда URL неправильно нормализуются в процессе кэширования. Обычно это выглядит как ключи кэша, содержащие что-то вроде //assets// вместо /assets/ или assets/.

Когда ваше приложение Vite PWA размещено на GitHub Pages с конфигурацией не корневого пути (используя base=/${{ github.event.repository.name }}/), несколько факторов могут способствовать этой проблеме:

  1. Путь регистрации сервис-воркера: Файл сервис-воркера регистрируется с определенным путем, но манифест предварительного кэширования может содержать URL с разными базовыми путями
  2. Кэширование во время выполнения: Во время выполнения запросы могут обрабатываться с неправильной нормализацией URL
  3. Стратегия ключей кэша: Стратегии ключей кэша Workbox по умолчанию могут правильно не обрабатывать преобразование базового URL

Эта проблема особенно влияет на развертывания на GitHub Pages, поскольку GitHub обслуживает контент из подкаталогов, и структура URL отличается от локальной разработки или развертываний в корневом каталоге.

Основные причины проблемы

1. Несоответствие конфигурации Vite Base

Конфигурация base в Vite напрямую влияет на то, как ссылаются на ресурсы и как сервис-воркер генерирует ключи кэша. Когда вы устанавливаете base=/${{ github.event.repository.name }}/, это влияет на:

  • Ссылки на ресурсы в HTML-файлах
  • Загрузку скрипта сервис-воркера
  • Генерацию манифеста предварительного кэширования

Однако сам сервис-воркер может не осознавать это преобразование базового пути при обработке запросов.

2. Расположение файла сервис-воркера

Сервис-воркеры работают в другом контексте, чем основная страница. Они регистрируются из определенного пути, но перехватывают запросы со всего источника (origin). Это разделение может вызывать проблемы с нормализацией URL.

3. Нормализация URL в Workbox

Workbox использует собственную логику нормализации URL, которая может не учитывать пользовательские базовые пути. Параметр urlManipulation в Workbox иногда может создавать непреднамеренные двойные слэши при обработке URL.

4. Специфическое поведение GitHub Pages

GitHub Pages обслуживает контент из подкаталогов и может обрабатывать завершающие слэши иначе, чем другие платформы хостинга. Это может вызывать у сервис-воркера восприятие URL иначе, чем в браузере.

Решения через конфигурацию

1. Модификация конфигурации Vite PWA

Обновите ваш vite.config.js, чтобы включить правильную обработку URL:

javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  plugins: [
    vue(),
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
      manifest: {
        name: 'Название вашего приложения',
        short_name: 'Приложение',
        description: 'Описание',
        theme_color: '#ffffff',
      },
      workbox: {
        // Настройка конфигурации workbox
        runtimeCaching: [
          {
            urlPattern: /^https:\/\/.*\.(?:js|css|png|jpg|jpeg|svg|gif|woff|woff2|ttf)$/,
            handler: 'CacheFirst',
            options: {
              cacheName: 'static-cache',
              expiration: {
                maxEntries: 100,
                maxAgeSeconds: 60 * 60 * 24 * 30, // 30 дней
              },
              cacheableResponse: {
                statuses: [0, 200],
              },
              // Добавляем манипуляцию с URL для предотвращения двойных слэшей
              urlManipulation: ({ url }) => {
                const pathname = url.pathname.replace(/\/+/g, '/')
                url.pathname = pathname.startsWith('/') ? pathname : `/${pathname}`
                return [url]
              },
            },
          },
        ],
      },
    }),
  ],
  base: process.env.NODE_ENV === 'production' 
    ? `/${process.env.VITE_APP_NAME}/` 
    : '/',
})

2. Конфигурация сборки для разных окружений

Создайте отдельные конфигурации сборки для разных окружений:

javascript
// vite.config.js
import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  
  return {
    base: env.VITE_BASE_URL || '/',
    plugins: [
      // ... ваши плагины
    ],
  }
})

Затем установите переменные окружения в ваших файлах .env:

  • .env.production: VITE_BASE_URL=/${process.env.GITHUB_REPOSITORY.replace('github.com/', '').split('/')[1]}/
  • .env.development: VITE_BASE_URL=/

3. Пользовательский файл сервис-воркера

Создайте пользовательский сервис-воркер, который обрабатывает нормализацию URL:

javascript
// public/sw.js
import { precacheAndRoute } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'
import { CacheFirst } from 'workbox-strategies'

// Получаем манифест предварительного кэширования из внедренной переменной
const precacheManifest = self.__WB_MANIFEST__

// Нормализуем URL для предотвращения двойных слэшей
const normalizeUrl = (url) => {
  try {
    const parsedUrl = new URL(url, self.location.href)
    pathname = parsedUrl.pathname.replace(/\/+/g, '/')
    parsedUrl.pathname = pathname.startsWith('/') ? pathname : `/${pathname}`
    return parsedUrl.toString()
  } catch (e) {
    return url
  }
}

// Предварительное кэширование с нормализованными URL
const normalizedManifest = precacheManifest.map(entry => ({
  ...entry,
  url: normalizeUrl(entry.url)
}))

precacheAndRoute(normalizedManifest)

// Регистрируем кэширование во время выполнения с манипуляцией URL
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new CacheFirst({
    cacheName: 'api-cache',
    plugins: [{
      cacheKeyWillBeUsed: async ({ request }) => {
        const normalizedUrl = normalizeUrl(request.url)
        return new Request(normalizedUrl, request)
      }
    }]
  })
)

Альтернативные подходы

1. Корректировка пути в GitHub Actions

Модифицируйте ваш workflow GitHub Actions для более тщательной обработки базового пути:

yaml
- name: Production Build
  run: |
    REPO_NAME="${{ github.event.repository.name }}"
    bun run build:prod -- --base="/${REPO_NAME}/"
    
- name: Upload to GitHub Pages
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./dist
    cname: your-domain.com
    keep_files: true

2. Пользовательская стратегия ключей кэша

Реализуйте пользовательскую стратегию ключей кэша, которая обрабатывает базовый путь:

javascript
// В вашей конфигурации Workbox
customCacheKey: ({ request, url, params }) => {
  // Удаляем двойные слэши и нормализуем путь
  const normalizedPath = url.pathname.replace(/\/+/g, '/')
  const cleanPath = normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`
  
  // Восстанавливаем URL с нормализованным путем
  const normalizedUrl = new URL(cleanPath, url.origin)
  
  // Добавляем префикс при необходимости для GitHub Pages
  const githubPagesPrefix = `/${process.env.VITE_APP_NAME || ''}/`
  if (normalizedUrl.pathname.startsWith(githubPagesPrefix)) {
    normalizedUrl.pathname = githubPagesPrefix + normalizedUrl.pathname.substring(githubPagesPrefix.length)
  }
  
  return normalizedUrl.toString()
}

3. Корректировка области видимости сервис-воркера

Настройте область видимости сервис-воркера в соответствии с вашей схемой развертывания:

javascript
// Регистрируем сервис-воркер с правильной областью видимости
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    const repoName = window.location.pathname.split('/')[1]
    const swUrl = `/sw.js` // или `/your-repo-name/sw.js`
    
    navigator.serviceWorker.register(swUrl, {
      scope: repoName ? `/${repoName}/` : '/'
    }).then(registration => {
      console.log('Успешная регистрация ServiceWorker с областью видимости:', registration.scope)
    }).catch(error => {
      console.log('Ошибка регистрации ServiceWorker:', error)
    })
  })
}

Тестирование и проверка

1. Инструменты для проверки кэша

Используйте DevTools браузера для проверки содержимого кэша:

  1. Откройте DevTools Chrome (F12)
  2. Перейдите в Application > Cache Storage
  3. Выберите ваш кэш и проверьте ключи
  4. Ищите записи с двойными слэшами

2. Отладка сервис-воркера

Добавьте отладку в ваш сервис-воркер:

javascript
// Добавьте в ваш сервис-воркер
console.log('Service Worker активирован')
console.log('Текущая область видимости:', self.scope)
console.log('Текущий URL:', self.location.href)

// Логируем операции кэширования
self.addEventListener('install', (event) => {
  console.log('Установка сервис-воркера с манифестом:', self.__WB_MANIFEST__)
})

self.addEventListener('activate', (event) => {
  console.log('Сервис-воркер активирован с областью видимости:', self.scope)
})

3. Тестовый скрипт для URL

Создайте тестовую страницу для проверки обработки URL:

javascript
// test-url-handling.js
const testUrls = [
  '/assets/script.js',
  '//assets/script.js',
  '/assets//script.js',
  'https://your-domain.com/your-repo-name/assets/script.js'
]

testUrls.forEach(url => {
  console.log(`Оригинал: ${url}`)
  try {
    const normalized = new URL(url, window.location.href)
    console.log(`Нормализован: ${normalized.pathname}`)
  } catch (e) {
    console.log(`Ошибка: ${e.message}`)
  }
})

Лучшие практики для развертывания на GitHub Pages

1. Последовательная конфигурация путей

Обеспечьте последовательную обработку путей во всех файлах конфигурации:

javascript
// Пример централизованной конфигурации путей
const GITHUB_PAGES_CONFIG = {
  enabled: process.env.NODE_ENV === 'production',
  repoName: process.env.GITHUB_REPOSITORY?.split('/').pop() || 'my-app',
  baseUrl: process.env.NODE_ENV === 'production' 
    ? `/${process.env.GITHUB_REPOSITORY?.split('/').pop()}/` 
    : '/'
}

export default defineConfig({
  base: GITHUB_PAGES_CONFIG.baseUrl,
  // ... остальная конфигурация
})

2. Управление переменными окружения

Настройте правильные переменные окружения:

bash
# .env.production
VITE_APP_NAME=my-app
VITE_BASE_URL=/my-app/
VITE_API_URL=https://api.example.com

# .env.development  
VITE_APP_NAME=my-app
VITE_BASE_URL=/
VITE_API_URL=http://localhost:3000

3. Тестовый пайплайн

Добавьте тестирование в ваш workflow GitHub Actions:

yaml
- name: Test Build
  run: |
    cd dist
    # Тестируем, что все ресурсы правильно ссылается
    grep -r "assets" index.html | head -5
    # Проверяем двойные слэши в HTML
    if grep -r "//" index.html | grep -v "http://" | grep -v "https://"; then
      echo "Обнаружены потенциальные проблемы с двойными слэшами"
      exit 1
    fi

4. Мониторинг и логирование

Реализуйте мониторинг для раннего обнаружения проблем с ключами кэша:

javascript
// Добавьте в ваш сервис-воркер
const CACHE_KEY_LOGGER = {
  logCacheOperation: (operation, url, cacheName) => {
    console.log(`[${new Date().toISOString()}] ${operation}: ${url} в ${cacheName}`)
    // Отправляем в сервис мониторинга при необходимости
  }
}

// Оборачиваем операции кэширования с логированием
self.addEventListener('fetch', (event) => {
  const url = event.request.url
  const normalizedUrl = url.replace(/\/+/g, '/')
  
  if (url !== normalizedUrl) {
    CACHE_KEY_LOGGER.logCacheOperation('URL_NORMALIZATION', url, normalizedUrl)
  }
})

Источники

  1. Документация Workbox - Ключи кэша и нормализация URL
  2. Руководство по конфигурации Vite Plugin PWA
  3. Лучшие практики развертывания на GitHub Pages
  4. Область видимости и контекст сервис-воркера
  5. Конфигурация кэширования во время выполнения Workbox

Заключение

Проблема двойных слэшей в ключах кэша Workbox для Vite PWA на GitHub Pages обычно возникает из-за проблем с нормализацией URL, когда приложение развертывается в подкаталоге. Ключевые выводы включают:

  1. Исправьте конфигурацию base в Vite, чтобы она соответствовала пути развертывания на GitHub Pages, и обеспечьте последовательность во всех ссылках
  2. Реализуйте пользовательскую манипуляцию с URL в конфигурациях Workbox для правильной обработки нормализации путей
  3. Используйте сборки для разных окружений для различной обработки URL в разработке и продакшене
  4. Регулярно проверяйте содержимое кэша с помощью DevTools браузера и пользовательских отладочных скриптов
  5. Мониторьте операции сервис-воркера для раннего обнаружения проблем с URL в процессе развертывания

Для немедленного решения начните с реализации пользовательской манипуляции с URL в вашей конфигурации Workbox и убедитесь, что все ключи кэша правильно нормализованы перед развертыванием в продакшене. Если проблема сохраняется, рассмотрите возможность создания пользовательского сервис-воркера, который обрабатывает специфические шаблоны URL вашего развертывания на GitHub Pages.

Ключевым является обеспечение того, чтобы понимание URL путей сервис-воркером точно соответствовало тому, как GitHub Pages обслуживает ваше приложение, предотвращая любые конфликты нормализации, которые приводят к двойным слэшам в ключах кэша.