文档
Next.js 博客首页 — SSR + ISR
目标
用 Next.js App Router 实现博客首页:服务端渲染文章列表,ISR 每 60 秒增量再生,同时包含客户端搜索框。
完整代码
src/app/page.tsx
import { Suspense } from 'react'
import SearchInput from './SearchInput'
// 模拟从数据库/API获取文章
async function getPosts(): Promise<Post[]> {
// 真实场景: return fetch('https://api.example.com/posts', { next: { revalidate: 60 } })
return [
{ id: 1, title: 'Next.js 15 新特性解读', author: '张三', date: '2024-12-01', tags: ['Next.js'] },
{ id: 2, title: 'React Server Components 深度解析', author: '李四', date: '2024-11-28', tags: ['React'] },
{ id: 3, title: '用 Prisma + PostgreSQL 构建全栈应用', author: '王五', date: '2024-11-20', tags: ['数据库'] },
]
}
interface Post {
id: number
title: string
author: string
date: string
tags: string[]
}
export default async function HomePage() {
const posts = await getPosts()
return (
<main className="min-h-screen bg-gray-50">
{/* 头部 */}
<header className="bg-white shadow-sm border-b">
<div className="max-w-3xl mx-auto px-4 py-8">
<h1 className="text-4xl font-extrabold text-gray-900">📝 我的博客</h1>
<p className="text-gray-500 mt-2">ISR 每 60s 更新 · App Router · RSC</p>
</div>
</header>
{/* 搜索栏(客户端组件) */}
<Suspense fallback={<div>加载搜索框...</div>}>
<SearchInput />
</Suspense>
{/* 文章列表(服务端渲染) */}
<section className="max-w-3xl mx-auto px-4 py-8 space-y-4">
{posts.map(post => (
<article
key={post.id}
className="bg-white rounded-xl shadow-sm border p-6 hover:shadow-md transition-shadow"
>
<h2 className="text-xl font-semibold text-gray-900 mb-2">{post.title}</h2>
<div className="flex items-center gap-3 text-sm text-gray-500">
<span>✍️ {post.author}</span>
<span>📅 {post.date}</span>
</div>
<div className="mt-3 flex gap-2">
{post.tags.map(tag => (
<span key={tag} className="bg-blue-100 text-blue-700 text-xs px-2 py-1 rounded-full">
{tag}
</span>
))}
</div>
</article>
))}
</section>
</main>
)
}
src/app/SearchInput.tsx
'use client'
import { useState } from 'react'
export default function SearchInput() {
const [query, setQuery] = useState('')
return (
<div className="max-w-3xl mx-auto px-4 py-4">
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="🔍 搜索文章..."
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 text-lg"
/>
{query && (
<p className="mt-2 text-sm text-gray-500">
搜索: <strong>{query}</strong> (客户端状态)
</p>
)}
</div>
)
}
运行
npm install && npm run dev
# → http://localhost:3000
预期输出
- 服务端渲染的文章列表(查看页面源代码可见完整 HTML)
- 客户端搜索框(使用
'use client'声明) - 60 秒 ISR 增量再生(
revalidate: 60)