核心是让占位符自然过渡到真实图片,不跳动、不闪烁、加载时机合理。关键在于结构准备、样式兜底和观察策略协同:HTML中预置带base64占位图的语义化占位符;CSS锁定容器宽高比并设淡入动画;IntersectionObserver配置rootMargin与threshold预加载;加载后校验naturalWidth并处理错误、现代格式及低电量模式等边界情况。

核心是让占位符自然过渡到真实图片,不跳动、不闪烁、加载时机合理。关键不在“切换”动作本身,而在于结构准备、样式兜底和观察策略的协同。
HTML 结构要提前预留空间与语义
占位符不能靠 JS 插入,必须在初始 HTML 中就存在,否则会触发布局抖动(layout shift)或 FOUC(Flash of Unstyled Content)。
- 用
src属性放一个极小的 base64 占位图(如 1×1 透明 GIF),确保浏览器识别为有效图片资源,避免默认图标或空白断裂 - 真实地址存于
data-src,不写srcset或sizes,等进入视口后再动态补全 - 添加
loading="lazy"作为降级兜底(现代浏览器自动启用原生懒加载,IntersectionObserver 加载失败时仍可保底) - 建议加上
decoding="async",让图片解码不阻塞主线程
CSS 必须锁定尺寸并提供视觉反馈
没有固定宽高,图片加载瞬间会撑开容器,造成页面跳动——这是用户感知卡顿的主因之一。
- 对图片容器(如
<figure></figure>或外层<div>)设置明确的 <code>aspect-ratio(推荐),或用padding-top百分比技巧维持宽高比 - 图片自身设
width: 100%; height: 100%; object-fit: cover;,确保铺满且不拉伸 - 用
background-color或background-image设置柔和灰阶占位背景,比纯白更友好 - 可加简单淡入动画:
transition: opacity 0.2s ease-in; opacity: 0.8;,加载完成再设opacity: 1 -
rootMargin: '0px 0px 200px 0px':向下扩展 200px,让图片在即将进入前就开始加载,消除滑动时的空白延迟 -
threshold: 0.05(即 5% 可见):比0更早触发,比0.1更保守,适合图文混排场景 - 加载后立即调用
observer.unobserve(img),避免重复回调和内存泄漏 - 监听
error事件,加载失败时替换为统一错误占位图,并移除data-src防止重试 - 真实
src赋值后,检查img.naturalWidth === 0,若为 0 表示加载失败(常见于 CORS 或路径错误) - 对 WebP/AVIF 等现代格式,可用
picture+source封装,data-src改为data-sources存 JSON 字符串,加载时解析并插入 - 首屏图片(
loading="eager")可跳过 Observer,直接用document.readyState === 'complete'后立即加载 - 支持低电量模式:检测
navigator?.battery?.saveData或matchMedia('(prefers-reduced-data)'),此时跳过高清图,只加载压缩版
IntersectionObserver 配置要兼顾性能与体验
不是“一进视口就加载”,而是预判用户滚动意图,平衡带宽与首屏速度。
加载逻辑需处理边界情况
真实环境里,网络、缓存、图片格式都可能出问题,不能只写“成功路径”。

















