📌 适用背景
正在开发一个网站,SEO 管理系统或,页面需要缓存但又不能太久失效。目标:
- 静态页面自动过期更新(避免旧数据)
- 加载速度快(靠缓存)
- 提供后门
/api/revalidate
接口用于立即刷新
1️⃣ ISR 的基础概念
Next.js 的 增量静态再生成(ISR) 允许页面:
- 在构建后第一次请求时生成(静态化)
- 被缓存下来(在内存中,或磁盘中)
- 按设定时间
revalidate
过期并再生成
2️⃣ 缓存机制详解
✅ 1. 页面级别 revalidate
设置页面过期时间方式:
plain
// page.tsx 或 layout.tsx 中
export const revalidate = 3600 // 1小时自动过期
或在 fetch()
时设置:
plain
await fetch(url, {
next: { revalidate: 3600 }, // 设置数据缓存时间
})
✅ 2. 缓存类型对比
缓存类型 | 存储位置 | 速度 | 是否持久化 | 是否受 revalidate 控制 |
---|---|---|---|---|
内存缓存 | 服务器内存 | 极快 | ❌(重启丢失) | ❌(时间不控制,仅清除后消失) |
磁盘缓存 | 文件系统 | 快 | ✅ | ✅(按 revalidate 过期) |
3️⃣ 配置方式对比
✅ 默认配置(含内存缓存)
plain
// next.config.ts
export default {
// 不设置 isrMemoryCacheSize,默认 50MB
}
plain
// page.tsx
export const revalidate = 3600
流程如下:
- 用户首次访问 → 页面生成,缓存到 内存和磁盘
- 后续访问(1小时内)→ 命中内存缓存(速度最快)
- 超过 1 小时 → 磁盘缓存过期,触发再生成
- 内存缓存仍可命中(直到内存满或重启丢失)
🚫 禁用内存缓存(推荐用于可控缓存策略)
plain
// next.config.ts
export default {
experimental: {
isrMemoryCacheSize: 0, // 关闭内存缓存
},
}
plain
// page.tsx
export const revalidate = 3600
流程如下:
- 页面只缓存到 磁盘
- 访问时从磁盘读取
- 磁盘缓存 1 小时后过期 → 触发再生成
适合场景:使用 Redis、Supabase 等外部缓存层或需要缓存可控性强的系统。
4️⃣ 实战应用:SEO 页面配置示例
✅ 页面级 revalidate 配置
plain
// app/[locale]/services/[slug]/page.tsx
export const revalidate = 3600 // 页面 1 小时后自动过期
export async function generateMetadata({ params }) {
const { locale } = params
const path = `/services/${params.slug}`
const seoData = await getSeoByPath(path, locale)
return {
title: seoData?.title ?? "默认标题",
description: seoData?.description ?? "",
}
}
✅ fetch
配置缓存时间
plain
// lib/seo.ts
export async function getSeoByPath(path: string, locale: string = 'en') {
const res = await fetch(
`${process.env.API_BASE_URL}/v1/sys/seo-config/path?path=${encodeURIComponent(path)}&locale=${locale}`,
{
headers: { 'Content-Type': 'application/json' },
next: { revalidate: 3600 }, // 1小时缓存
}
)
return res.ok ? res.json() : null
}
5️⃣ 增加 /api/revalidate 接口(可选)
用于手动触发页面再生成,比如管理员更新了 SEO 配置。
plain
// app/api/revalidate/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(req: NextRequest) {
const secret = req.nextUrl.searchParams.get('secret')
const path = req.nextUrl.searchParams.get('path')
if (secret !== process.env.REVALIDATE_SECRET || !path) {
return NextResponse.json({ error: 'Invalid request' }, { status: 401 })
}
try {
await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}${path}`, {
headers: {
'x-prerender-revalidate': '1', // 可选,仅标记用途
},
})
return NextResponse.json({ revalidated: true, path })
} catch (err) {
return NextResponse.json({ error: 'Revalidate failed' }, { status: 500 })
}
}
⚠️ 部署时注意保护接口安全,可使用 token 或 IP 限制。
6️⃣ 最佳实践总结
目标 | 推荐配置说明 |
---|---|
🚀 兼顾性能与实时性 | 使用默认配置,保留内存缓存,页面设置 revalidate |
📡 高实时性(强一致性) | 禁用内存缓存 + 设置较短 revalidate |
🔄 后台更新时立即刷新页面 | 提供 /api/revalidate 接口 |
🧠 多租户/自定义缓存系统 | isrMemoryCacheSize: 0 禁用内存缓存,结合 Redis 等方案 |
✅ 推荐配置示例(SEO 系统)
plain
// next.config.ts
import createNextIntlPlugin from "next-intl/plugin"
export default createNextIntlPlugin()({
reactStrictMode: true,
output: 'standalone',
transpilePackages: ['@my-monorepo/ui'],
images: {
domains: ['0.assets.sunionfab.com', 'ufc-oversea.oss-eu-central-1.aliyuncs.com'],
},
experimental: {
isrMemoryCacheSize: 0, // 禁用内存缓存
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})
return config
},
})
🧩 总结一句话:
设置 revalidate
控制页面过期时间,是否启用 isrMemoryCacheSize
取决于你对缓存性能与一致性的需求。