thinkphp分页必须由paginate()主动控制查询链,不可先select()再分页;需透传query参数保留搜索条件;模板中用{$list->render()}输出分页html,不可用{$page}或{$list->show()}。

ThinkPHP分页不是“查完再切”,而是由 paginate() 主动控制查询链;漏掉 query 参数或误用 select() 会导致翻页丢条件、总数不准、性能崩塌。
paginate() 必须链式调用,不能先 select() 再分页
常见错误是把分页当成数组处理:Db::name('user')->where(...)->select()->paginate(10)。这会让全表数据先加载进 PHP 内存,paginate() 只是对数组切片——既没走数据库 LIMIT,COUNT 也不准,翻页变卡顿。
- 正确写法:让
paginate()接管整个查询链,例如Db::name('user')->where('status', 1)->order('id desc')->paginate(15) - 底层会自动执行两条 SQL:
COUNT(*)算总数 + 带LIMIT的主查询 - 模型调用同理:
UserModel::where('deleted_at', null)->paginate(10) - 含
join或子查询时,确保关联字段有索引,否则 COUNT 可能慢到超时
翻页后搜索条件丢失?一定是 query 参数没透传
带关键词搜索的列表点“下一页”回到第一页、keyword 消失——90% 是因为没显式传 query 参数。框架默认只读 $_GET['page'],其他参数如 keyword、type 不会自动带上。
- 安全写法:
->paginate(10, false, ['query' => request()->param()]),透传全部 GET 参数 - 精准控制(推荐):
['query' => ['keyword' => input('keyword/s', ''), 'type' => input('type/s', '')]],避免传入token等敏感字段 - 如果用
input('get.'),需手动过滤键名,防止 URL 注入(比如禁止传__method)
模板里怎么取数据和渲染分页?别用 {$page} 或 {$list->show()}
控制器 assign 的变量名(比如 list)就是分页对象本身,它既是数据集合,也自带方法。模板里不拆包、不二次赋值。
- 循环数据:
{volist name="list" id="item"}{$item.title}{/volist}(list是你 assign 的变量名) - 输出分页 HTML:
{$list->render()},不是{$page},也不是{$list->show()} - 拿元信息:
{$list->total()}(总条数)、{$list->currentPage()}(当前页)、{$list->lastPage()}(总页数) - 样式定制优先复制
vendor/topthink/framework/src/view/paginate.tpl到app/view/paginate.tpl,再在config/app.php中配置'template'路径
API 场景或 POST 请求下,page 参数必须手动指定
paginate() 默认从 $_GET['page'] 读页码,但接口走 POST、或前端用 JSON 提交时,这个值永远是空,结果永远显示第一页。
- 显式传参:
->paginate(10, false, ['page' => input('p/d', 1)]),把p当作页码字段 - 或统一用
input('page/d', 1),但需确认前端是否配合传该 key - RESTful 接口建议用路径参数(如
/api/users?page=2),保持语义清晰且兼容性好
最易被忽略的是:分页对象的 render() 输出的是完整 HTML 片段,不是字符串拼接逻辑;一旦自定义模板没配对或路径错,就静默失败——连报错都不抛,只会看到空白分页栏。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











