Как я сделал Next.js и PWA, наконец

Как я сделал 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


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE