wyjaśnia, jak strona dokumentacji osiąga szybkie statyczne ładowanie stron, zachowując funkcjonalność trybu edycji CMS
Ten przewodnik wyjaśnia, w jaki sposób strona dokumentacji osiąga szybkie statyczne ładowanie stron, jednocześnie zachowując funkcjonalność trybu edycji CMS.
Używamy dwóch tras dla tej samej zawartości:
app/
├── [...slug]/page.tsx # Produkcja: force-static (szybko)
└── preview/[...slug]/page.tsx # Tryb edycji: force-dynamic (odczytuje searchParams)
Pośrednik proxy w warstwie middleware przezroczysto przepisuje żądania trybu edycji:
Żądanie użytkownika: /en/headless/quickstart?edit_mode=true
↓
Proxy wykrywa ?edit_mode=true
↓
Wewnętrzne przepisanie na: /preview/en/headless/quickstart?edit_mode=true
↓
Dynamiczna trasa renderuje z opakowaniami edycji
Użytkownik nigdy nie widzi /preview w swoim adresie URL – to wewnętrzne przepisanie.
// app/[...slug]/page.tsx
export const dynamic = 'force-static';
export const revalidate = 60;
export default async function Page({ params }) {
// Brak searchParams – strona jest w pełni statyczna
// app/preview/[...slug]/page.tsx
export const dynamic = 'force-dynamic';
export default async function PreviewPage({ params, searchParams }) {
// searchParams dostępne – można wykryć edit_mode
return <ParametricRoutePage params={params
?edit_mode=true z adresu URL// src/proxy.ts
export const proxy = async (request: NextRequest) => {
const { pathname, searchParams } = request.nextUrl;
const editMode = searchParams.get('edit_mode');
| Scenariusz | Czas odpowiedzi | Renderowanie |
|---|---|---|
| Strona produkcyjna (z pamięci podręcznej) | ~50-100 ms | Statyczna z CDN |
| Strona produkcyjna (przeterminowana) | ~50-100 ms + odświeżenie w tle | Statyczna, potem ISR |
| Tryb edycji | ~500-1500 ms | Dynamiczne SSR |
Ustawienie revalidate = 60 włącza ISR. To NIE oznacza, że strony są przeliczane co 60 sekund.
ISR wykorzystuje schemat stale-while-revalidate:
Żądanie przychodzi:
→ Czy zbuforowana strona ma mniej niż 60 s? → Dostarcz z pamięci podręcznej (natychmiast)
→ Czy zbuforowana strona ma więcej niż 60 s? → Dostarcz nieświeżą pamięć podręczną (natychmiast)
+ odśwież w tle na potrzeby następnego żądania
Brak żądań = brak ponownego przeliczania. Nigdy.
Przykład:
Okno 60 sekund to zabezpieczenie. Idealnie używalibyśmy odświeżania na żądanie poprzez webhooki, gdy zmienia się zawartość w CMS.
| Przypadek użycia | URL | Użyta trasa |
|---|---|---|
| Zwykłe przeglądanie | /en/headless/quickstart | Statyczna |
| Kreator szablonów CMS | /en/headless/quickstart?edit_mode=true | Podgląd (przez przepisanie) |
| Podgląd bloku AI | /en/headless/quickstart?ai_preview=1 | Podgląd (przez przepisanie) |
Proste podejście (bez force-static, bez trasy podglądu) działa, ale jest wolne:
// Proste podejście – działa, ale ~1-2 s na żądanie
export default async function Page({ params, searchParams }) {
return <ParametricRoutePage params={params} searchParams={searchParams} />;
}
To renderuje dynamicznie przy każdym żądaniu. W przypadku strony z dokumentacją z wieloma stronami oznacza to:
Architektura dwóch tras daje nam to, co najlepsze z obu światów: statyczną szybkość dla czytelników i dynamiczną funkcjonalność dla redaktorów.