图片懒加载的实现方案?IntersectionObserver 怎么用?
方案 1:原生 loading 属性(最简单)
<img src="image.jpg" loading="lazy" alt="..." />- 浏览器原生支持,零 JS
- 兼容性:Chrome 76+、Firefox 75+、Safari 15.4+
- 简单场景推荐
方案 2:IntersectionObserver(推荐)
const images = document.querySelectorAll('img[data-src]')
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src // 加载真实图片
img.removeAttribute('data-src')
obs.unobserve(img) // 加载后取消观察
}
})
}, {
rootMargin: '200px' // 提前 200px 开始加载
})
images.forEach(img => observer.observe(img))<img data-src="image.jpg" src="placeholder.jpg" alt="..." />IntersectionObserver API
const observer = new IntersectionObserver(callback, options)
// options
{
root: null, // 视口(默认)或指定容器
rootMargin: '0px', // 根元素的 margin
threshold: 0.1 // 交叉比例达到 10% 时触发
}
// callback
(entries) => {
entries.forEach(entry => {
entry.isIntersecting // 是否可见
entry.intersectionRatio // 交叉比例 0~1
entry.target // 被观察的元素
})
}
// 方法
observer.observe(el) // 开始观察
observer.unobserve(el) // 停止观察
observer.disconnect() // 停止所有观察方案 3:scroll 事件 + getBoundingClientRect(传统)
function lazyLoad() {
const images = document.querySelectorAll('img[data-src]')
const scrollTop = window.pageYOffset
const windowHeight = window.innerHeight
images.forEach(img => {
if (img.offsetTop < scrollTop + windowHeight + 200) {
img.src = img.dataset.src
img.removeAttribute('data-src')
}
})
}
window.addEventListener('scroll', throttle(lazyLoad, 200))
lazyLoad() // 初始检查⚠️ 性能差:scroll 事件频繁触发,offsetTop 触发重排
应用场景
- 图片懒加载
- 无限滚动列表
- 组件懒渲染(滚到可见区域才渲染)
- 广告/视频按需加载
- 动画触发(元素进入视口时播放)
// 无限滚动
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
loadMore()
}
})
observer.observe(document.querySelector('.sentinel'))最佳实践
- 简单场景 →
loading="lazy" - 需要控制加载时机 → IntersectionObserver
- 兼容旧浏览器 → polyfill 或 scroll 方案
- 搭配占位图/骨架屏提升体验