博客列表
博客列表页的展示逻辑、筛选功能和分页机制
Hey,同学!这一篇我们来看看博客列表页是怎么工作的——也就是用户在博客首页看到的那些文章卡片们~ 🃏
🤔 列表页做什么?
博客列表页的核心任务是:
- 展示文章:把数据库里的文章变成漂亮的卡片
- 分页:文章太多时,分成多页展示
- 筛选:让用户按分类、标签筛选
- 排序:按时间、热度排序
💡 想象一下:列表页就像「书店的展示架」——把最好的书摆出来,让顾客容易找到!
📁 相关文件
text
apps/blog/src/app/
├── page.tsx # 列表页(首页)
├── loading.tsx # 加载状态
└── api/blog/posts/route.ts # 文章 API
🏗️ 展示逻辑
1. 获取数据
在 Server Component 中获取文章列表:
typescript
// page.tsx
import { getPosts } from '@/lib/posts';
export default async function BlogPage() {
// 获取文章,参数包括分页、筛选、排序
const { items: posts, count: total } = await getPosts({
page: 1,
limit: 10,
sortBy: 'created_at',
sortOrder: 'desc',
});
return (
<main>
<PostList posts={posts} total={total} />
<Pagination total={total} pageSize={10} />
</main>
);
}
2. 为什么这样设计?
Server Components 获取数据:
- 数据在服务器端获取,客户端只需渲染
- 减少客户端 JavaScript,提升性能
- 更好地做 SEO(搜索引擎能看到完整内容)
分页逻辑:
- 默认每页 10 篇文章
- 支持 URL 参数切换页面(
?page=2) - 预加载相邻页面,数据更流畅
🎨 卡片设计
文章卡片包含什么?
text
┌─────────────────────────────────┐
│ ┌─────────────┐ 分类 [🔖] │
│ │ 封面图 │ │
│ └─────────────┘ │
│ │
│ 文章标题 │
│ 文章摘要... │
│ │
│ 🏷️ 标签1 🏷️ 标签2 │
│ 📅 2024-01-15 👁️ 1234 │
└─────────────────────────────────┘
设计考量
| 元素 | 作用 |
|---|---|
| 封面图 | 视觉吸引,一眼看出文章风格 |
| 分类标签 | 快速了解文章类型 |
| 标题 | 最关键的信息 |
| 摘要 | 帮助用户判断是否感兴趣 |
| 标签 | 相关主题,一键筛选 |
| 日期/阅读量 | 时效性和热度参考 |
🔧 分页机制
URL 驱动分页
分页参数通过 URL 传递,SEO 友好:
text
/blog # 第 1 页
/blog?page=2 # 第 2 页
/blog?page=3 # 第 3 页
分页组件
typescript
<Pagination
currentPage={page}
totalPages={Math.ceil(total / pageSize)}
baseUrl="/blog"
/>
预加载优化
- 用户在第 1 页时,后台预加载第 2 页
- 点击下一页时,内容已经准备好了
- 体验「丝滑」般流畅!
##筛筛选功能
按分类筛选
text
/blog?category=技术
点击分类标签,URL 变化,列表自动更新。
按标签筛选
text
/blog?tag=React
实现原理
筛选参数都在 URL 中:
- 服务器根据参数查询数据库
- 筛选条件作为 SQL 的 WHERE 子句
- URL 可分享,SEO 友好
📊 排序方式
| 方式 | 说明 |
|---|---|
| 最新优先 | 按发布时间倒序(默认) |
| 最早优先 | 按发布时间正序 |
| 更新时间 | 按最后修改时间 |
| 阅读量 | 按点击/浏览次数 |
💡 性能优化
1. 静态生成 + ISR
列表页使用 ISR(增量静态再生):
typescript
export const revalidate = 60; // 每 60 秒重新生成
- 首页每分钟自动更新
- 不需要每次请求都查数据库
- 速度快!
2. 图片优化
- 使用
next/image自动优化 - 懒加载,只在视口内加载
- WebP/AVIF 格式,省流量
3. 骨架屏
加载时显示骨架屏:
typescript
// loading.tsx
<div className="skeleton-card" />
🔗 下一步
🃏 好的列表页就像「会说话的书架」——让用户一眼就找到想看的内容!