Html::build()生成分页静态页时,第三个参数必须是以public/开头的绝对路径,如"public/static/news/page_1.html";否则文件虽能写入但无法被Web服务器访问,导致404。

Html::build() 生成分页静态页时路径必须带 public/ 前缀
直接调用 Html::build() 生成分页 HTML 文件,第三个参数必须是绝对路径且以 public/ 开头,比如 "public/static/news/page_1.html"。漏掉 public/,文件虽能写入磁盘,但 Web 服务器无法直连——Nginx/Apache 默认只服务 public/ 下的资源,访问时必然返回 404。
常见错误现象:file_put_contents("static/news/page_1.html", $html) 看似逻辑通顺,实则生成路径在项目根目录,完全不可达。
- 必须用
public/static/...或public/news/...这类真实可被 Web 访问的路径 - 生成前检查目录是否存在:
mkdir('public/static/news', 0755, true) - 不要依赖插件自动创建父级目录——
topthink/think-html不处理目录创建,只写文件
分页链接必须全部指向静态路径,不能留动态 query 参数
生成了 page_1.html、page_2.html,但模板里「下一页」还写的是 ?page=2,那用户点进去还是走 PHP 脚本,静态化等于白做。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在模板中封装函数:
get_static_page_url($page_num),内部判断是否启用静态模式,返回/news/page_{$page_num}.html或/news/?page={$page_num} - 分页渲染不能直接用
$list->render()—— 它输出的是动态 URL,需重写分页 HTML 拼接逻辑 - 首页要统一为
/news/或/news/index.html,避免/news/?page=1和/news/index.html重复收录
批量生成前必须重置 $_GET 并手动注入 page 参数
ThinkPHP 的 paginate() 默认读取 $_GET['page'],如果在 CLI 或后台脚本中循环生成分页,不清理 $_GET,所有页都会拿到同一个 page 值(通常是 1 或空)。
正确做法是显式传参、绕过全局变量:
- 用
Db::name('article')->paginate(10, false, ['page' => $p])强制指定当前页码 - 或临时覆盖:
$_GET['page'] = $p;,但必须在每次循环开始前重置,且确保后续无其他逻辑污染 - 模板中禁止出现
$_SERVER['REQUEST_URI']或$_SERVER['QUERY_STRING']—— 这些在 CLI 下为空,会导致链接丢失或报错
数据更新后不能全量重建,得用增量策略
文章新增或修改后,重新生成全部分页 HTML 是低效且危险的:100 页 × 每页 2s = 200s,期间页面可能部分失效;磁盘空间也容易被旧文件占满。
更稳妥的做法:
- 只重建受影响的页:比如某篇文章更新,只重生成它所在的那一页(通过
Db::name('article')->where('id', $id)->value('page')反查页码) - 记录每页最后生成时间戳,用
touch('public/static/news/page_1_updated_at.txt')标记,便于监控和清理 - 上线部署脚本中加一步:
array_map('unlink', glob('public/static/news/page_*.html'));,但仅限于明确触发静态重建的场景,别放在线上定时任务里乱删
真正难的不是生成,而是让静态页和动态数据状态长期对齐——URL 规则、分页容量、数据排序字段,只要其中任何一个变了,旧静态页就变成脏数据,且很难自动发现。



















