📚 Next.js 分页 & 模糊搜索:在无限数据海里优雅地翻页

🌊 一、序章:当列表无限,服务器汗流浃背

想象你在做一个博客网站,用 Next.js 写 API,数据库里有十万篇文章。

如果你一次性在 SSR 阶段把全部文章 fetch 回来,那服务器会用一种哲学的口吻警告你:

"孩子,分页是对性能的尊重。"

于是,当数据塞满栈顶,我们学会了分页(Pagination)。

接着,用户又提出新需求:

"我想模糊搜一下标题里含'GPT'的文章。"

于是我们又召唤了模糊搜索(Fuzzy Search)。

这两个家伙一结合,就成了 Web 中"可伸缩的数据接口黄金搭档"。


📦 二、分页的本质:切片,送到用户嘴边 🍰

分页不只是"每页10条"的UI技巧,而是一次数据切片的哲学动作

在底层,它往往体现为这样的流程:

arduino 复制代码
Client → API Route → DB Query (limit, offset)  
│  
▼  
返回特定数据集

分页的核心参数:

参数 含义 示例
page 当前页码 1, 2, 3, ...
limit 每页数据条数 10
offset 数据偏移量 = (page - 1) × limit 20

⚙️ 三、在 Next.js 中优雅地分页

我们可以使用 App Router + Prisma 作为例子。

js 复制代码
// /app/api/posts/route.js
import { prisma } from "@/lib/prisma";

export async function GET(req) {
  const { searchParams } = new URL(req.url);
  const page = parseInt(searchParams.get("page")) || 1;
  const limit = parseInt(searchParams.get("limit")) || 10;
  const skip = (page - 1) * limit;

  const posts = await prisma.post.findMany({
    skip,
    take: limit,
    orderBy: { createdAt: "desc" },
  });

  const total = await prisma.post.count();

  return Response.json({
    data: posts,
    meta: {
      page,
      limit,
      total,
      totalPages: Math.ceil(total / limit),
    },
  });
}

💡 注释点评:

  • skip 控制从第几条数据开始取;
  • take 控制一次抓多少条;
  • orderBy 保证翻页的排序稳定(别让同一篇文章在前后页间乱跳 🧨)。

📖 小图例:分页请求生命周期

sql 复制代码
+-----------------+
| 用户点击下一页 ▶ |
+--------┬--------+
         │ fetch /api/posts?page=2
         ▼
+-----------------------+
| Next.js Route Handler |
+--------┬--------------+
         │ Prisma Query (skip=10, take=10)
         ▼
+-----------------------+
| 返回JSON响应 |
+-----------------------+

"分页不是切割,而是节奏。"

------ 一位数据库诗人


🔍 四、模糊搜索:让数据库懂点"人话"

搜索的本质是信息检索(Information Retrieval)。
模糊搜索就是在告诉数据库:

"别太死板,我记不太清楚关键字了,你帮我猜猜?"

在 SQL 环境中,它通常基于 LIKE 关键字实现(或更高级的 Full Text Index)。

案例:Next.js + Prisma 实现模糊搜索

php 复制代码
// /app/api/posts/search/route.js
import { prisma } from "@/lib/prisma";

export async function GET(req) {
  const { searchParams } = new URL(req.url);
  const q = searchParams.get("q") || "";
  const page = parseInt(searchParams.get("page")) || 1;
  const limit = parseInt(searchParams.get("limit")) || 10;
  const skip = (page - 1) * limit;

  const articles = await prisma.post.findMany({
    where: {
      title: {
        contains: q,  // 关键:模糊匹配
        mode: "insensitive", // 忽略大小写
      },
    },
    skip,
    take: limit,
    orderBy: { createdAt: "desc" },
  });

  const total = await prisma.post.count({
    where: {
      title: {
        contains: q,
        mode: "insensitive",
      },
    },
  });

  return Response.json({
    data: articles,
    meta: {
      search: q,
      page,
      limit,
      total,
      totalPages: Math.ceil(total / limit),
    },
  });
}

关键参数:

  • contains = 模糊查找
  • mode: 'insensitive' = 大小写不敏感
    这让你的搜索框具备了"生活中搜索栏那种聪明感"。

🤹‍♀️ 五、分页 + 模糊搜索的协奏曲

翻页时带着模糊条件,就像点菜时要服务员记住"少糖但加珍珠"。

请求示例👇

bash 复制代码
GET /api/posts/search?q=next&page=2&limit=10

即:

搜索标题中包含 "next" 的内容,

并返回第2页的10条结果。

这在前端中可以这么写:

javascript 复制代码
// /app/posts/page.jsx
'use client';
import { useState, useEffect } from 'react';

export default function PostList() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  const [q, setQ] = useState("");

  useEffect(() => {
    fetch(`/api/posts/search?q=${q}&page=${page}&limit=10`)
      .then(res => res.json())
      .then(json => setData(json.data));
  }, [page, q]);

  return (
    <div>
      <input
        placeholder="🔍 搜索文章..."
        value={q}
        onChange={(e) => setQ(e.target.value)}
      />
      <ul>
        {data.map(post => <li key={post.id}>{post.title}</li>)}
      </ul>
      <button onClick={() => setPage(p => p - 1)} disabled={page === 1}>上一页</button>
      <button onClick={() => setPage(p => p + 1)}>下一页</button>
    </div>
  );
}

结果就是一个简洁的、反应灵敏的模糊分页搜索系统


📊 六、性能与底层机制:别让数据库走神

分页与模糊搜索在底层数据结构上的区别:

技术层面 关键点 性能瓶颈
LIMIT + OFFSET 简单高效,分页首选 OFFSET数大时查询慢
LIKE '%key%' 通配符匹配,灵活 缺少索引,扫描量大
Full Text Search 借助倒排索引,提高模糊性能 建索引成本高
Cursor-based Pagination 使用游标代替页码 更适合集合更新频繁场景

建议:

  • 内容量较小 → OFFSET 模式足够
  • 内容量巨大 → 使用 游标分页全文索引方案

🧠 技术不是黑魔法,而是不断优化的折中哲学。


🎨 七、视觉层可视化:分页就像翻书页 📖

ini 复制代码
+--------------------------------------------------+
| [1] [2] [3] [4] ... [99] ▶︎                    |
|   🔍 搜索: [next-js 分页教程]                    |
+--------------------------------------------------+
| title: Next.js pagination tutorial               |
| title: Fuzzy search in Next.js                   |
| title: Building scalable API routes              |
| ...                                              |
+--------------------------------------------------+

每次点击"下一页",客户端只是修改了 URL 参数:

ini 复制代码
?page=3&q=fuzzy

而不是重新下载整个宇宙的数据。

这是 Web 的浪漫:

"按页面取数据,就像翻一本书,而不是重刷整个世界。"


⚡ 八、代码之外的思考:分页是一种文明

分页不仅是优化流量,更是一种数字伦理------

在信息过载的时代,我们不该一口吞下数据库。

模糊搜索,则是对人类记忆模糊性的技术礼让。

🪶 "我们不强行要求用户记得确切的词,而是让机器学会理解'大概'。"

这是计算机科学的温柔表达:

让算法去适应人,而不是让人适应算法。


🧭 九、总结清单

概念 核心要点 应用层
分页 LIMIT + OFFSET,控制数据流量 API层性能优化
模糊搜索 LIKE / contains + 字符串匹配 用户体验
组合使用 搜索结果分页展示 综合实战
优化思路 游标 + 索引 + 缓存 可扩展架构
相关推荐
Mintopia4 小时前
⚖️ AIGC版权确权技术:Web内容的AI生成标识与法律适配
前端·javascript·aigc
周家大小姐.4 小时前
vue实现模拟deepseekAI功能
前端·javascript·vue.js
小张成长计划..4 小时前
前端7:综合案例--品优购项目(HTML+CSS)
前端·css·html
一个处女座的程序猿O(∩_∩)O4 小时前
React 多组件状态管理:从组件状态到全局状态管理全面指南
前端·react.js·前端框架
鹏多多4 小时前
用useTransition解决React性能卡顿问题+实战例子
前端·javascript·react.js
只愿云淡风清4 小时前
ECharts地图数据压缩-ZigZag算法
前端·javascript·echarts
亿元程序员5 小时前
都2025年了,还有面试问A*寻路的???
前端
Moment5 小时前
Node.js v25.0.0 发布——性能、Web 标准与安全性全面升级 🚀🚀🚀
前端·javascript·后端
杨超越luckly5 小时前
HTML应用指南:利用POST请求获取中国一汽红旗门店位置信息
前端·arcgis·html·数据可视化·门店数据