 
                        
                    Как я сделал Next.js и PWA, наконец
10 августа 2025 г.Мне нравится создавать инструменты, которые не просто работают - ониубирайсяПолем Моя команда была в окопах следующего. JS достаточно долго, чтобы точно знать, что больно. Одна из последних вещей, которые я собрал, - этоnext-pwa-pack-Пакет, которая использует полную поддержку PWA в вашем приложении Next.js, не вырывая волосы.
Предыстория (aka: почему я написал эту вещь)
Каждый раз, когда клиент упоминал «поддержку PWA», я готовился.
Я пробовал существующие библиотеки. Слишком много магии. Тонны конфигурации. Или просто совершенно несовместимо сМаршрутизатор приложения- Что, кстати, мы полностью приняли. Я хотел:
- Ревализация кэша на стороне сервера.
- Интеграция маршрутизатора приложений.
- Легкая синхронизация между вкладками.
- Чистый API для управления кэшем из бэкэнда.
Вместо этого я закончил писать работников услуг вручную. Настройка кэша TTLS. Работа с логикой обновления. Управление несвежными клиентами. Вручную вытирать кэши каждый раз, когда мы отправлялись.
И даже не заставляйте меня начинать с пользователей, которые не видят обновления, пока они не зарегистрировались. Достаточно было достаточно.
Мне нужно было что-то мертвое простое, предсказуемое и проверенное в бою. Итак, я построил это.
Зданиеnext-pwa-pack: Что в него вшло
Первый шаг написал минимальный работник службы:
- Кэши HTML с TTL.
- Обработка статических активов.
- Работает офлайн, как настоящий PWA.
Затем я добавил систему обмена сообщениями, чтобы клиент мог поговорить с обслуживающим работником - например, чтобы разорвать кэш или полностью отключить кэширование.
Далее я написал несколько сценариев:
- Автокопияsw.jsВmanifest.json, иoffline.htmlв ваш проект.
- Введите действие сервера под названиемrevalidatePWAчто вы можете использовать где угодно (маршруты API, действия сервера, компоненты сервера - выбирайте).
Для полного маршрутизатора приложений и поддержки SSR/Edge я обернул все в функцию высшего порядка:withPWAПолем Один импорт, один вызов - сделано.
Я также встроил синхронизацию вкладок. Потому что пользователиволяОткройте свое приложение на 3 вкладках и ожидайте, что они волшебными обновлениями синхронизируются. Я решил это черезlocalStorage + storageсобытия.
Результат? Пакет, который работает. Нет конфигурации черной магии. Нет переписывания основных частей вашего приложения.
Чем вы получаетеnext-pwa-pack
После установки вы получите:
- Регистрация работника обслуживанияИз коробки - нет шаблона.
- Оффлайн запаснойс настраиваемымoffline.htmlПолем
- Автопопированные файлыВы можете настроить (Manifest, SW и т. Д.).
- Кэш управление API- Очистить, обновить, отключить, все программно.
- Синхронизация между вкладками-Нет устаревшего контента в многосторонних настройках.
- ПанельДля штата PWA в реальном времени во время местного развития.
- Серверная переоценкаПоддержка через серверные действия, маршруты API или внешние интеграции WebHook.
Вы можете взять пакет здесь: 👉https://github.com/dev-family/next-pwa-pack
Что происходит, когда вы его устанавливаете
Установка сценария Auto-Copies PWA Cowerplate в/public:
- sw.js- Ваш обслуживающий работник с кеш -логикой.
- offline.html- Страница задолженности для автономного режима.
- manifest.json- Направьте его в соответствии с вашим приложением.
⚠ Существующие файлы не будут перезаписаны - он уважает вашу настройку.
Если вы хотите запустить копию вручную:
node node_modules/next-pwa-pack/scripts/copy-pwa-files.mjs
# or
npx next-pwa-pack/scripts/copy-pwa-files.mjs
Действие сервераrevalidatePWAтакже добавлен в вашapp/actions.tsилиsrc/app/actions.tsФайл в зависимости от структуры папки:
"use server";
export async function revalidatePWA(urls: string[]) {
 const baseUrl = process.env.NEXT_PUBLIC_HOST || "http://localhost:3000";
 const res = await fetch(`${baseUrl}/api/pwa/revalidate`, {
   method: "POST",
   headers: { "Content-Type": "application/json" },
   body: JSON.stringify({
     urls,
     secret: process.env.REVALIDATION_SECRET,
   }),
 });
 return res.json();
}
Если этот файл не отображается, вы можете запустить:
node node_modules/next-pwa-pack/scripts/copy-pwa-server-actions.mjs
Настройка вашегоmanifest.json
После установки не забудьте настроить/public/manifest.json:
{
 "name": "My App",
 "short_name": "App",
 "description": "My amazing PWA app",
 "start_url": "/",
 "display": "standalone",
 "background_color": "#ffffff",
 "theme_color": "#000000",
 "icons": [
   {
     "src": "/icons/icon-192x192.png",
     "sizes": "192x192",
     "type": "image/png"
   },
   {
     "src": "/icons/icon-512x512.png",
     "sizes": "512x512",
     "type": "image/png"
   }
 ]
}
Бросить свои значки вpublic/icons/, или изменить пути выше. Ничего особенного.
Быстрый старт: подключите его
Заверните макет вPWAProviderи волшебство начинается:
import { PWAProvider } from "next-pwa-pack";
export default function layout({ children }) {
  return <PWAProvider>{children}</PWAProvider>;
}
Если вы хотите, чтобы переоценка работала со стороны сервера, вам также необходимо обновить промежуточное программное обеспечение:
// /middleware.ts
import { withPWA } from "next-pwa-pack/hoc/withPWA";
function originalMiddleware(request) {
  // your logic here
  return response;
}
export default withPWA(originalMiddleware, {
  revalidationSecret: process.env.REVALIDATION_SECRET!,
  sseEndpoint: "/api/pwa/cache-events",
  webhookPath: "/api/pwa/revalidate",
});
export const config = {
  matcher: ["/", "/(ru|en)/:path*", "/api/pwa/:path*"],
};
Варианты HOC:
- originalMiddleware: Ваше базовое промежуточное программное обеспечение (например, для i18n или auth).
- revalidationSecret: Секрет токен, чтобы заблокировать повторную переоценку кеша.
- sseEndpoint: SSE Stream Path (измените, если он конфликт).
- webhookPath: Конечная точка, чтобы нажимать для запуска обновления кэша (используется- revalidatePWA)
ВнутриPWAProvider
АPWAProviderКомпания куча вещей под капюшоном-и вы также можете компоненты вишни:
RegisterSW
Автоматически регистрирует обслуживающий работник (/sw.js) Утеряет ошибки. Вы можете переопределить путь, если это необходимо:
<PWAProvider swPath="/custom/sw.js">{children}</PWAProvider>
CacheCurrentPage
Перехватывает навигацию (включая переходы в стиле SPA), кэширует HTML текущей страницы.
SWRevalidateListener
Смотрируют события LocalStorage и запускают обновление кэша по вкладкам.
SSERevalidateListener
Слушает события сервера изsseEndpointПолем Когда ваш бэкэнд говорит «переосмысление этих URL -адресов», этот слушатель гарантирует, что клиенты делают это.
DevPWAStatus
Панель только для DEV можно включить так:
<PWAProvider devMode>{children}</PWAProvider>
Шоу:
- Онлайн/статус офлайн
- Кэш -версия
- Обновление доступности
- Инструменты одного щелчка: очистить кеш, unregister SW, обновить, отключить/включить кеш
Что на самом деле делает обслуживающий работник
Ядроsw.jsРучки:
Html кэширование
- Страницы, кэшированные с TTL (по умолчанию: 10 мин - настройку вsw.js)
- Авторевалифицирует, когда TTL истекает
- Автономный резерв, если HTML отсутствует
Статические активы
- JS, CSS, изображения кэшируютсянавсегда
- Только кэши получают запросы
Поддержка сообщений
Поддерживает эти действия от клиента:
- CACHE_CURRENT_HTML
- REVALIDATE_URL
- DISABLE_CACHE/- ENABLE_CACHE
- SKIP_WAITING
- CLEAR_STATIC_CACHE
Автономный режим
- Служитoffline.htmlЕсли сеть и кэш оба не сняты
- Пытается обновляться, когда вернется в Интернет
С использованиемwithPWAв промежуточном программном обеспечении
Вот гдеnext-pwa-packДействительно зарабатывает. Это приносит кеш -переосмысление в SSR и промежуточное программное обеспечение Edge - с поддержкой SSE и все.
export default withPWA(originalMiddleware, {
  revalidationSecret: process.env.REVALIDATION_SECRET!,
  sseEndpoint: "/api/pwa/cache-events",
  webhookPath: "/api/pwa/revalidate",
});
Параметры:
- originalMiddleware: Ваша существующая логика промежуточного программного обеспечения (Auth, i18n и т. Д.)
- revalidationSecret: Значит, никто другой не может ткнуть ваш кеш
- sseEndpoint: переопределить, если что -то еще использует этот маршрут
- webhookPath: Используется сервером или внешними системами для повторной проверки конкретных URL -адресов
Реальные варианты использования
Обновление кэша после изменений данных
import { updateSWCache } from "next-pwa-pack";
// After creating a blog post:
const handleCreatePost = async (data) => {
  await createPost(data);
  updateSWCache(["/blog", "/dashboard"]);
};
Обновление кэша с сервера
import { revalidatePWA } from "../actions";
await createPost(data);
await revalidatePWA(["/my-page"]);
Очистка кэша в выходе
import { clearAllCache } from "next-pwa-pack";
const handleLogout = async () => {
  await logout();
  await clearAllCache();
  router.push("/login");
};
Все действия клиентского кеша
import {
  clearAllCache,
  reloadServiceWorker,
  updatePageCache,
  unregisterServiceWorkerAndClearCache,
  updateSWCache,
  disablePWACache,
  enablePWACache,
  clearStaticCache,
  usePWAStatus,
} from "next-pwa-pack";
// Examples:
await clearAllCache();
await reloadServiceWorker();
await updatePageCache("/about");
await unregisterServiceWorkerAndClearCache();
await clearStaticCache();
updateSWCache(["/page1", "/page2"]);
disablePWACache();
enablePWACache();
const { online, hasUpdate, swInstalled, update } = usePWAStatus();
Маршрут API для триггеров внешнего кеша
Если вы хотите запустить обновления кэша извне (например, с панели администратора), вот маршрут API, который вы можете использовать:
// app/api/webhook/revalidate/route.ts
import { NextRequest, NextResponse } from "next/server";
import { revalidatePWA } from "@/app/actions";
import { revalidateTag } from "next/cache";
import { FetchTags } from "@/app/api/endpoints/backend";
export async function POST(request: NextRequest) {
  try {
    const { tags, secret, urls } = await request.json();
    if (secret !== process.env.REVALIDATION_SECRET) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }
    const validTags = Object.values(FetchTags);
    const invalidTags = tags?.filter((tag) => !validTags.includes(tag)) || [];
    if (invalidTags.length > 0) {
      return NextResponse.json(
        { error: `Invalid tags: ${invalidTags.join(", ")}` },
        { status: 400 }
      );
    }
    let successful = 0;
    let failed = 0;
    if (tags?.length) {
      const tagResults = await Promise.allSettled(
        tags.map((tag) => revalidateTag(tag))
      );
      successful = tagResults.filter((r) => r.status === "fulfilled").length;
      failed = tagResults.filter((r) => r.status === "rejected").length;
    }
    if (urls?.length) {
      await revalidatePWA(urls);
    }
    return NextResponse.json({
      success: true,
      message: "Cache revalidation completed",
      tags,
      urls,
      successful,
      failed,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error("Webhook revalidation error:", error);
    return NextResponse.json({ error: "Internal server error" }, { status: 500 });
  }
}
Ударить его:
POST https://your-app.com/api/webhook/revalidate
{
  "tags": ["faq"],
  "secret": "1234567890",
  "urls": ["/ru/question-answer"]
}
Отладка и devtools
Вот что вы можете проверить при отладке:
- Идти вDevtools → Приложение → Служба работниковПолем
- Подтвердите, что работник зарегистрирован.
- ПроверятьКэш-хранение → HTML-Cache-V2Чтобы увидеть, кэшируются ли страницы.
- Моделировать офлайн вСеть → Оффлайни перезагрузить. Вы должны увидетьoffline.htmlПолем
- Консольные журналы от обслуживающего работника тоже помогают:- [PWA] Service Worker registered
- [SW] Cached: /about
- [SW] Revalidated and updated cache for: /blog
 
GOTCHAS & NOTES
Несколько вещей, которые вы должны знать, прежде чем отправить:
Безопасность
- PWA требует HTTPS в производстве.
- Только получение запросов кэшируется.
- Не кэшируйте конфиденциальные данные.
Производительность
- Пакет не касается базовой линии производительности вашего приложения.
- Это значительно улучшает повторные нагрузки.
Конфигурация
- TTL установлен вsw.js(По умолчанию: 10 минут).
- Вы можете исключить URL -адреса из кэширования черезCACHE_EXCLUDEПолем
- manifest.jsonнужно адаптировать к вашему приложению.
- revalidatePWAДействие редактируется - настраивайте его по мере необходимости.
- withPWAи- PWAProviderОба принимают варианты:
export default function PWAProvider({
  children,
  swPath,
  devMode = false,
  serverRevalidation = {
    enabled: true,
    sseEndpoint: "/api/pwa/cache-events"
  },
}: PWAProviderProps) {
Что дальше
next-pwa-packнаписан дляNext.js 15Полем Это должно работать надNext.js 13 приложение маршрутизаторКроме того - просто не тестируется тщательно.
Запланированные функции:
- Конфигурация TTL через параметры (без редактированияsw.js)
- Push -уведомления
- Управление кэшем на основе шаблона
- Показатели производительности для эффективности кэша
Вот и все.
Если вы устали от споров с работниками вручную вручную, дайтеnext-pwa-packвыстрел. Вы перейдете от нуля к полной поддержке PWA в одном кофейном перерыве.
Вопросы, ошибки или отзывы? Откройте проблему или ударите нас.
👉 github.com/dev-family/next-pwa-pack
Оригинал
