ThinkPHP 8.0 静态缓存不自动支持分页,需手动缓存原始数据集或HTML片段;推荐缓存查询结果数组并手动构造分页参数,避免序列化Paginator对象导致错误。

ThinkPHP 8.0 的静态缓存(cache() 函数或 Cache 门面)本身不自动缓存分页结果,因为 Paginator 对象是动态构建的,包含当前请求上下文(如页码、参数、URL),直接缓存它不仅无效,还可能引发序列化错误或分页逻辑错乱。要真正“缓存分页数据”,必须明确区分两个层面:缓存原始数据集(SQL 查询结果) 和 缓存分页 HTML 渲染片段(可选)。以下是安全、实用、符合 TP8.0 最佳实践的设置方式:
✅ 缓存分页查询的原始数据(推荐主用)
核心思路:把 paginate() 前的查询条件固化为缓存键,只缓存 items() 数据 + 总数,再手动组装 Paginator 实例。
use think\facade\Cache;
use app\model\UserModel;
$cacheKey = 'user_list_' . md5(serialize([
'status' => 1,
'keyword' => input('keyword/s', ''),
'size' => $size = input('size/d', 15),
'page' => $page = input('page/d', 1)
]));
$list = Cache::remember($cacheKey, 3600, function () use ($size, $page) {
// 注意:此处必须用未执行的 Query 构造器,不能先 select()
$query = UserModel::where('status', 1);
if ($kw = input('keyword/s')) {
$query->where('name', 'like', "%{$kw}%");
}
// 关键:先获取总数(可单独缓存)
$total = $query->count();
// 再查当前页数据(跳过 COUNT(*),用 limit+offset)
$data = $query->order('id desc')
->limit(($page - 1) * $size, $size)
->select()
->toArray();
// 手动构造轻量 Paginator(无需完整实例,只需关键字段)
return [
'data' => $data,
'total' => $total,
'per_page' => $size,
'current_page' => $page,
'last_page' => (int) ceil($total / $size),
'has_more' => $page * $size < $total
];
});⚠️ 说明:这种方式绕开了
paginate()的自动 COUNT,避免卡顿;缓存的是纯数组,稳定可靠;模板中用{$list.data}循环,分页 HTML 需手写或调用自定义渲染函数。
✅ 缓存分页 HTML 片段(适合低频变动列表)
若页面结构固定、筛选条件简单,可对 render() 输出做片段缓存:
立即学习“PHP免费学习笔记(深入)”;
$renderKey = 'user_paginate_html_' . request()->url(true);
$html = Cache::remember($renderKey, 600, function () use ($list) {
// $list 是正常 paginate() 返回的 Paginator 对象
return $list->render();
});
$this->assign('page_html', $html);✅ 适用场景:后台管理页、公告列表等更新不频繁的页面
❌ 不适用:实时性要求高、搜索条件多变、用户个性化列表
⚠️ 绝对不要做的三件事
- 不要直接
cache('key', $list)——Paginator对象含闭包、资源句柄,无法序列化 - 不要在
paginate()外层套cache(),如cache('list', UserModel::paginate(15))—— 每次都重建对象,缓存失效 - 不要缓存
count()结果却不缓存数据 —— 会导致总数与当前页数据不一致(例如中间有新增/删除)
? 配合优化:开启 simple 模式 + 手动总数缓存
对超大数据表,建议组合使用:
// 总数单独缓存(带更新钩子更佳)
$total = Cache::remember('user_total_count', 7200, function () {
return UserModel::where('status', 1)->count();
});
// 分页查询启用 simple 模式(跳 COUNT)
$list = UserModel::where('status', 1)
->order('id desc')
->paginate(['list_rows' => 20, 'simple' => true]);
// 模板中显示缓存的总数:共 {$total} 条这样既提速,又保留总数展示能力。
不复杂但容易忽略。



















