
Как я сделал 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
Оригинал