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 <blockquote><p>⚠️ 说明:这种方式绕开了 <code>paginate()</code> 的自动 COUNT,避免卡顿;缓存的是纯数组,稳定可靠;模板中用 <code>{$list.data}</code> 循环,分页 HTML 需手写或调用自定义渲染函数。</p></blockquote><h3>✅ 缓存分页 HTML 片段(适合低频变动列表)</h3><p>若页面结构固定、筛选条件简单,可对 <code>render()</code> 输出做片段缓存:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站"><img
src="https://img.php.cn/upload/skill/000/000/081/179040786932301.jpg" alt="btpanel phpsite 宝塔面板PHP网站" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站" class="overflowclass">btpanel phpsite 宝塔面板PHP网站</a>
<p class="overflowclass">宝塔面板 PHP 网站管理:站点创建、删除、启停、PHP 版本切换、域名管理、SSL证书管理、伪静态管理、数据库管理</p>
</div>
<a rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><pre class="brush:php;toolbar:false;">$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} 条
这样既提速,又保留总数展示能力。
不复杂但容易忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










