ThinkPHP 6.x 列表分页必须用 paginate(),因其自动执行 COUNT 和 LIMIT 两次查询并封装为带 render() 等方法的 Paginator 对象;手动 limit/offset 仅得数组,调 render() 会报错。

ThinkPHP 6.x 的列表分页必须用 paginate(),不能用 limit() + offset() 手动算 —— 否则分页信息(总条数、当前页、页码数组)全丢,render() 渲染直接报错或空白。
为什么 paginate() 是唯一可靠方式
ThinkPHP 的分页不是“只查一页数据”,而是自动执行两次查询:一次查总记录数(COUNT(*)),一次查当前页数据(带 LIMIT)。只有 paginate() 能把这两次结果封装成带 render()、lastPage()、hasPages() 等方法的分页对象。手动拼 limit/offset 只能得到原始数据数组,没分页元信息,模板里调 $list->render() 会抛出 Call to a member function render() on array 错误。
常见错误写法:
// ❌ 错误:返回的是数组,不是分页对象
$list = Db::name('user')->limit($limit)->offset($offset)->select();
paginate() 基础用法(控制器层)
在控制器中调用 paginate() 时,传入每页数量即可,框架自动读取 page 参数(默认 GET 参数名是 page),无需手动解析 URL 或判断空值。
立即学习“PHP免费学习笔记(深入)”;
-
paginate(10)表示每页 10 条,当前页由$_GET['page']决定,默认为 1 - 支持链式条件,如
where('status', 1)->order('id desc')->paginate(15) - 若需自定义分页参数名(比如用
p代替page),加第 2 个参数:paginate(10, false, ['query' => ['p' => input('p')]]) - 注意:第 2 个参数设为
false表示不自动追加隐藏域(适用于纯 API 场景);设为true(默认)则生成带隐藏字段的表单分页
示例(控制器):
use think\facade\Db;
public function index()
{
$list = Db::name('article')
->where('is_delete', 0)
->order('create_time DESC')
->paginate(8); // ✅ 自动处理分页逻辑
return view('index', ['list' => $list]);
}
模板中正确渲染分页(视图层)
模板里拿到的 $list 是 think\Paginator 实例,可直接调用其方法。重点不是“怎么显示页码”,而是“别漏掉数据遍历和分页渲染两个环节”。
- 遍历数据必须用
$list->items()或直接{$list}(VList 模板引擎下等价于$list->items()) - 分页 HTML 必须调用
$list->render(),它输出的是完整 HTML 字符串(含<ul class="pagination"></ul>等) - 如果页面无样式,不是代码错,而是你没引入 CSS ——
render()不带默认样式,需自己写或配theme - 检查是否启用了模板布局:若用了
layout,确保render()输出位置不在被截断的区块内
示例(index.html):
{volist name="list" id="vo"}
<div class="item">
<h3>{$vo.title}</h3>
<p>{$vo.content|mb_substr=0,100}...</p>
</div>
{/volist}
{if !empty($list)}
<div class="pagination">{$list->render()}</div>
{/if}
容易被忽略的三个坑
实际部署时,这三个点最容易导致分页失效或样式错乱:
-
paginate()在关联查询中可能报错:例如with('category')+paginate(),需改用load()或先查主表再批量关联,否则 COUNT 查询会因 JOIN 失败 - 自定义查询构造器(如用
Db::query()写原生 SQL)不支持paginate(),必须用Db::table()->where()->paginate()这类 Query Builder 链式调用 - Apache 环境下,URL 中含中文或特殊字符时
page参数可能被截断,建议统一用url()助手函数生成分页链接:{$list->render('default', ['type' => 'bootstrap'])}并配好路由规则
分页本身不复杂,但它的耦合点很隐蔽 —— 数据获取、参数解析、模板渲染、样式注入、路由兼容,任意一环断开都会让页码“消失”或“点不动”。动手前先确认你用的是 Query Builder 而非原生 SQL,再检查模板里是否真的调了 render(),而不是只写了循环。



















