Update Flow — Полный код¶
Уровень: L3 (deep-dive) | ← Назад к PWA
Файл: frontend/src/hooks/useServiceWorker.ts¶
Хук управления жизненным циклом обновлений Service Worker.
import { useEffect, useState } from 'react';
import { registerSW } from 'virtual:pwa-register';
export function useServiceWorker() {
const [needRefresh, setNeedRefresh] = useState(false);
useEffect(() => {
// Регистрация SW через vite-plugin-pwa virtual module
const updateSW = registerSW({
// Вызывается когда новая версия SW готова к активации
onNeedRefresh() {
setNeedRefresh(true); // Показать пользователю промпт
},
// Вызывается когда app готов к offline-работе (первая установка)
onOfflineReady() {
console.log('[SW] App ready to work offline');
},
});
// Cleanup: отменить регистрацию при unmount
return () => {
updateSW?.();
};
}, []);
/** Применить обновление: перезагрузить страницу */
const applyUpdate = () => {
setNeedRefresh(false);
window.location.reload(); // Новый SW активируется при reload
};
/** Отклонить обновление (пользователь решит позже) */
const dismissUpdate = () => {
setNeedRefresh(false);
};
return { needRefresh, applyUpdate, dismissUpdate };
}
Как пользователь узнаёт об обновлении¶
Поток:¶
sequenceDiagram
participant Deploy as CI/CD Deploy
participant CDN as CDN / Hosting
participant SW as Service Worker
participant Hook as useServiceWorker
participant UI as Update Prompt
Deploy->>CDN: Новый build (new sw.js)
Note over SW: Браузер периодически проверяет sw.js
SW->>CDN: GET /sw.js (byte-compare)
CDN-->>SW: Новая версия!
SW->>SW: install event (precache new assets)
SW->>Hook: onNeedRefresh()
Hook->>Hook: setNeedRefresh(true)
Hook->>UI: Render prompt
alt User clicks "Update"
UI->>Hook: applyUpdate()
Hook->>SW: window.location.reload()
Note over SW: skipWaiting → activate → control
else User dismisses
UI->>Hook: dismissUpdate()
Note over Hook: Обновление применится при следующем визите
end

Пользовательский опыт:¶
- Пользователь открывает приложение
- SW обнаруживает новую версию в фоне
- Появляется ненавязчивый промпт "Доступно обновление"
- Пользователь может:
- Обновить сейчас →
reload()→ новая версия - Позже → промпт закрывается, обновится при следующем визите
Интеграция в приложение¶
Хук используется в корневом компоненте App.tsx:
function App() {
const { needRefresh, applyUpdate, dismissUpdate } = useServiceWorker();
return (
<>
{/* ...роутинг... */}
{needRefresh && (
<UpdatePrompt
onUpdate={applyUpdate}
onDismiss={dismissUpdate}
/>
)}
</>
);
}
Virtual Module: virtual:pwa-register¶
Этот модуль генерируется vite-plugin-pwa на этапе сборки. Он:
- Регистрирует Service Worker (
navigator.serviceWorker.register()) - Настраивает
registerType: 'autoUpdate'— автоматическийskipWaiting+clients.claim - Предоставляет callbacks:
onNeedRefresh()— новый SW установлен, ждёт активацииonOfflineReady()— все ресурсы закешированыonRegistered(registration)— SW успешно зарегистрированonRegisterError(error)— ошибка регистрации
Почему autoUpdate?¶
| Стратегия | Поведение | Для Plechiki |
|---|---|---|
prompt |
Показать промпт, пользователь решает | — |
autoUpdate |
Обновить автоматически при следующей загрузке | ✅ |
С autoUpdate новый SW активируется при reload. Комбинация с onNeedRefresh позволяет показать промпт, но обновление всё равно произойдёт при следующем визите если пользователь откажется.