直接用 src 会导致图片立即加载,因浏览器解析 HTML 时即发起请求;懒加载需用 data-src 存真实地址,由 JS 检测视口后赋值给 src。

为什么直接用 src 会导致图片不懒加载
ThinkPHP 本身不处理前端渲染逻辑,它只负责把 HTML 字符串或模板变量输出到浏览器。如果你在模板里写 <img src="{$item.cover}">,PHP 渲染完就变成真实 src,浏览器一拿到 HTML 就立刻发起请求——根本没机会等滚动到视口再加载。
懒加载本质是前端行为,必须把真实地址存在别的属性里(比如 data-src),靠 JS 检测位置后手动赋值给 src。ThinkPHP 要做的,只是配合输出这个带 data-src 的结构,别提前捅破它。
- 错误写法:
<img src="{$item.cover}">→ 图片立即加载 - 正确写法:
<img class="lazy">→ 留给 JS 处理 - 如果用了 CDN 或缩略图,确保
{$item.cover}是最终可访问的 URL,不是相对路径或未解析的占位符
如何在 ThinkPHP 模板中安全输出 data-src
ThinkPHP 的模板引擎默认会对变量做 HTML 转义,防止 XSS,但这也可能把 URL 中的 & 变成 &,导致链接失效。尤其当图片 URL 带参数(如 ?x-oss-process=...)时容易出问题。
- 用
{$item.cover|raw}绕过转义(ThinkPHP 6/7) - 避免用
{:url()}拼接图片路径,除非你明确知道该函数返回的是完整 URL;多数情况下封面图是数据库存的原始路径,直接输出即可 - 加个空
src占位或用 1×1 透明图,防止 layout shift:<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazy">
JS 怎么配合 ThinkPHP 输出的 DOM 实现懒加载
不能依赖 jQuery 插件(如 lazysizes),因为 ThinkPHP 不内置 JS 运行时。你需要自己写轻量逻辑,或引入一个无依赖的库。核心是监听 scroll + intersectionObserver,检测元素是否进入视口。
立即学习“PHP免费学习笔记(深入)”;
- 推荐用原生
IntersectionObserver,兼容性到 Chrome 51+/Safari 12.1+,老版本可降级为 scroll + getBoundingClientRect - 观察器初始化要等 DOM 加载完,不要放在
<head>里执行 - 示例片段(放在模板底部或独立 JS 文件):
<script>
document.addEventListener('DOMContentLoaded', () => {
const lazyImages = document.querySelectorAll('img.lazy');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.dataset.src;
if (src) {
img.src = src;
img.classList.remove('lazy');
observer.unobserve(img);
}
}
});
});
lazyImages.forEach(img => observer.observe(img));
});
</script>
服务端要不要配合做响应式图片或占位图
ThinkPHP 可以在输出前做简单处理,但不宜承担图片压缩、格式转换等职责。重点是让前端拿到的信息足够灵活。
- 如果数据库存的是原始大图路径,建议在模板里用
str_replace或自定义模板函数生成 WebP/缩略图 URL(例如:{$item.cover|replace='original.jpg','thumb.jpg'|raw}) - 不要在控制器里用 GD/Imagick 实时生成缩略图——高并发下 CPU 和 IO 压力大,应由 Nginx 或 CDN 完成
- 若需服务端 fallback(如 JS 关闭时显示默认图),可在模板加
noscript:<noscript><img src="{$item.cover|raw}"></noscript>
真正卡住懒加载效果的,往往不是 ThinkPHP 怎么写,而是图片 URL 是否可访问、JS 是否执行成功、以及 data-src 有没有被意外转义。先确保这三点,再谈优化。



















