ThinkPHP5.1分页必须用paginate()链式调用,不可先select();控制器中直接Db或模型调用paginate(),模板用{volist}遍历$list并{$list->render()}输出分页;需通过query参数透传搜索条件;大数据量时应禁用总数统计、启用simple分页并加复合索引。

在ThinkPHP5.1中实现数据分页,必须用paginate()链式调用,不能先select()再分页——否则会把全表数据拉进PHP内存,一翻页就卡死,总数不准,性能崩塌。
控制器里正确调用paginate()
第一步:在控制器方法中,直接对Db查询或模型调用paginate(),传入每页条数。
【必须链式调用,不可中断】错误写法:Db::name('user')->where('status', 1)->select()->paginate(10);这已执行完查询,paginate()只是对PHP数组切片,完全失去数据库层优化。
正确写法:$list = Db::name('user')->where('status', 1)->order('id desc')->paginate(15);
立即学习“PHP免费学习笔记(深入)”;
模型写法同理:$list = User::where('status', 1)->order('create_time desc')->paginate(10);
第二步:将分页对象赋值给模板变量,不是数组也不是分页HTML字符串,而是整个Paginator实例。
$this->assign('list', $list);
模板中渲染数据和分页栏
数据循环用{volist}标签,直接遍历$list变量即可,无需解包或取data子键。
{volist name="list" id="item"}
{$item.name} — {$item.email}
{/volist}
分页HTML输出必须调用render()方法,且不能加|raw过滤器(除非你手动拼接了HTML字符串)。
{$list->render()}
注意:不要写成{$page}、{$list->show()}或{$list|raw}——这些都不生效或报错。
保留搜索参数不丢失
方法一:透传全部GET参数
在控制器paginate()第三个参数中加入query配置:
$list = Db::name('article')->where('status', 1)->paginate(10, false, ['query' => request()->param()]);
方法二:精准控制传参(推荐用于含敏感字段的场景)
$keyword = input('keyword/s', '');
$type = input('type/s', '');
$list = Article::where('status', 1)
->where('title', 'like', "%{$keyword}%")
->paginate(10, false, ['query' => ['keyword' => $keyword, 'type' => $type]]);
【不加query参数,翻页后所有搜索条件都会清空】点“下一页”回到第一页、keyword消失,八成就是漏了这一步。
高性能场景下的深度优化
当数据量超过十万级,翻到第200页明显变慢,根本原因是MySQL执行LIMIT 9990, 20时被迫扫描并丢弃前9990行。
第一步:关闭totalCount统计,跳过COUNT(*)全表扫描
$list = Db::name('order')->where('user_id', $uid)->paginate(20, false);
第二步:改用simple分页模式,避免依赖总页数
{$list->simple()->render()}
第三步:补上appends()保持参数延续性
{$list->simple()->appends(request()->param())->render()}
第四步:检查并建立复合索引,确保WHERE + ORDER能被同一索引覆盖
例如:ALTER TABLE `order` ADD INDEX idx_uid_ctime (user_id, create_time DESC);



















