图片按需渲染核心是“不进视口不加载”,通用方案用data-src+getBoundingClientRect判断,现代推荐IntersectionObserver;Layui表格需在done回调中限定范围调用;Vue中须避免响应式提前触发,改用占位+进入视口再赋值。

图片按需渲染,核心是“不进视口不加载”。只要图片还没出现在用户能看到的区域,就让它保持占位状态,不发请求、不占资源。关键不在图片多不多,而在什么时候触发加载——必须等它真正“即将可见”才行动。
用 data-src + JS 判断可视位置
这是最通用、兼容性最好的方式:
- 模板中写:
<img src="" alt="描述">,src留空或设为小占位图,真实地址存在data-src里 - 页面加载完后,遍历所有
img[data-src],调用getBoundingClientRect()检查top 是否成立 - 满足条件就赋值
img.src = img.dataset.src,再删掉data-src避免重复加载 - 滚动时加节流(如
setTimeout防抖),只在滚动停止后批量检测,避免频繁计算
用 IntersectionObserver 自动监听
现代浏览器推荐方案,更轻量、更精准:
- 创建观察器:
new IntersectionObserver(cb, { threshold: 0.1 }),表示元素10%进入视口就触发 - 对每个带
data-src的img调用observer.observe(img) - 回调里判断
entry.isIntersecting为true时,把data-src赋给src,并调用unobserve解除监听 - 不用手动计算位置,也不依赖滚动事件,性能开销极低
Layui 表格里特别注意时机和范围
表格渲染是异步的,DOM生成晚,懒加载必须卡准节点:
- 一定要在
table.render()或table.reload()的done回调里调用flow.lazyimg() - 限定作用范围,比如
flow.lazyimg({ elem: '#myTable img[data-src]' }),避免扫到其他模块的图片 - 确保
.layui-table-body有固定高度且overflow-y: auto,移动端还要加-webkit-overflow-scrolling: touch - 每次 reload 后必须重新调用,否则新插入的行里图片不会被识别
Vue 场景下别让组件提前触发加载
Vue 的响应式会让:src一绑定就发请求,必须切断这个链路:
- 不要直接写
:src="item.url",改用:src="item.loaded ? item.url : placeholder" - 配合
v-intersection指令或自定义 hook,在元素进入视口时才设item.loaded = true - 虚拟滚动列表中,懒加载逻辑应放在每个
<listitem></listitem>内部,由 item 自己控制——滚动组件只管“要不要渲染”,不插手“要不要加载” - 避免在父组件统一监听所有图片,既失去粒度,又干扰滚动性能


















