paginate() 是绝大多数场景下唯一推荐的分页方法,它自动处理页码、条数、总数、URL生成及COUNT查询;手动LIMIT/OFFSET易出错且缺失分页元数据。

直接用 paginate() 就行,别手写 LIMIT/OFFSET
绝大多数场景下,paginate() 是唯一该用的方法——它自动处理当前页码、每页条数、总记录数、URL 生成和数据库 COUNT 查询。手动拼 skip/take 或 LIMIT + OFFSET 不仅容易出错,还会漏掉分页器所需的元数据(比如总页数、是否有下一页)。
常见错误现象:App\Models\User::query()->skip(10)->take(10)->get() 返回了 10 条数据,但没带分页信息,前端无法渲染页码栏;或者忘记传 $request->page 导致永远只查第一页。
-
paginate()默认读取page查询参数,每页 15 条(可改) - 它会执行两条 SQL:一条
COUNT(*),一条带LIMIT/OFFSET的主查询 - 如果确定总数不重要(如日志表超大),可用
simplePaginate()跳过 COUNT,性能更好
自定义每页数量和查询参数名
默认每页 15 条、参数叫 page,但实际项目常要改。比如后台列表习惯用 per_page=20 和 current_page=3,或 API 接口要求参数名是 offset/limit。
注意:参数名改了,前端 URL 也得同步改,否则 paginate() 拿不到当前页码,始终返回第一页。
立即学习“PHP免费学习笔记(深入)”;
- 改每页数量:
User::paginate(20)或User::paginate(20, ['*'], 'page', $request->input('page', 1)) - 改参数名:
User::paginate(10, ['*'], 'current_page')→ 对应 URL/users?current_page=2 - 手动传页码值(适合 API):
User::paginate(10, ['*'], 'page', $request->input('page', 1))
分页结果在 Blade 中怎么用
返回的是 LengthAwarePaginator 实例,Blade 模板里直接遍历 $users 即可,分页链接用 links() 渲染。别试图用 foreach 手动拼 HTML 分页按钮——样式、禁用状态、URL 参数都已内置处理好。
容易踩的坑:在控制器里调用 $users->toArray() 再传给视图,会导致分页器丢失,links() 报错或不显示;或者用了 simplePaginate() 却还调 render(),页面可能报“Cannot use object of type Illuminate\Pagination\LengthAwarePaginator as array”。
- 控制器中:
return view('users.index', ['users' => User::paginate(10)]); - Blade 中遍历:
@foreach($users as $user) {{ $user->name }} @endforeach - Blade 中渲染分页:
{{ $users->links() }}(自动适配 Bootstrap / Tailwind 样式) - 需要 JSON 响应?用
$users->toJson(),不是json_encode($users)—— 后者会暴露内部属性
大数据量时 COUNT 慢怎么办
当表有千万级记录,paginate() 的 COUNT(*) 可能拖慢响应。这不是 Laravel 的问题,而是 MySQL 对大表 COUNT 的固有瓶颈。强行优化 COUNT(比如用估算值)风险高,优先考虑替代方案。
真实项目里更常见的解法是放弃精确总页数,而非硬啃 COUNT 性能问题。
- 换用
simplePaginate():只查当前页数据 + 是否有下一页,跳过 COUNT,响应快几倍 - 加覆盖索引:确保
WHERE条件字段和排序字段都有联合索引,COUNT 能走索引扫描 - 缓存总数:如果数据更新不频繁,用
Cache::remember('users_count', 3600, fn() => User::count()),但要注意缓存失效时机 - 避免在复杂 JOIN 查询上分页:先用子查询或临时表把 ID 列出来再分页,减少 COUNT 的扫描范围
分页本身不是目的,用户要的是快速加载当前页内容。COUNT 慢时,宁可隐藏总页数,也不要让首屏等待超过 1 秒。



















