Bootstrap图片占位符需用CSS固定宽高+灰色背景(#e9ecef)防布局抖动,推荐ratio类配合::before伪元素实现响应式占位,加载失败时通过onerror添加img-broken类显示fallback图标,懒加载下应默认启用占位并解耦加载生命周期。
bootstrap 本身不提供图片加载占位符(placeholder)功能,img 标签也没有内置“加载中”状态。所谓“灰色方块”,其实是靠 css 强制固定宽高 + 背景色模拟出来的视觉占位,本质是提前预留空间、防止布局抖动。
常见错误是只加 width 和 height 属性却不设 background-color,结果图片加载前留白一片,用户误以为没内容;或者用 object-fit: cover 但容器没设宽高,导致占位失效。
img 或其父容器明确的 width 和 height(推荐用类如 ratio ratio-1x1 或自定义 class)background-color: #e9ecef(Bootstrap 的 $gray-200)模拟默认占位色,比纯白更显“未加载”src 指向空字符串或 data: 协议——部分浏览器会触发 404 请求或报 Failed to load resource
ratio 类配合伪元素占位Bootstrap 5 内置的 ratio 工具类能基于 padding-top 实现响应式宽高比容器,适合封面图、头像等场景。但它不自动渲染占位背景,需手动补一层 ::before。
典型使用场景:卡片列表里的商品图、文章缩略图,需要统一比例且不能因图片加载慢而撑开布局。
ratio ratio-4x3,再加自定义 class 如 img-placeholder
.img-placeholder::before 为绝对定位、全覆盖、带背景色的伪元素img 本身设 position: relative; z-index: 1;,确保加载后盖住占位层aspect-ratio 支持较晚,ratio 类在旧版仍更稳妥.img-placeholder::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #e9ecef;}
网络中断、路径写错、CORS 限制都可能导致 img 加载失败,此时仅靠灰色块不够友好。Bootstrap 没有 onerror 封装,得靠原生事件或 CSS :not(:loading) 配合 JS。
最轻量的做法是内联 onerror,但要注意它不会触发 CSS 动画,也不能复用逻辑;更健壮的是用 IntersectionObserver + img.naturalWidth === 0 判断失败。
<img src="..." onerror="this.classList.add('img-broken'); this.alt='图片加载失败'">
.img-broken { background: #f8f9fa url('data:image/svg+xml;utf8,<svg...>') center/1.5rem no-repeat; }
onerror 里反复设置 src,可能造成无限循环请求crossorigin="anonymous",否则 naturalWidth 在某些情况下读不到loading="lazy")与占位符的冲突点启用原生懒加载后,img 进入视口前不会触发 load 事件,但占位符需在首屏就可见。如果只依赖 JS 监听 load 去移除占位样式,首屏图片可能长期卡在灰色块状态。
关键不是禁用懒加载,而是让占位逻辑和加载生命周期解耦:占位样式默认开启,加载成功或失败后统一清除。
img.addEventListener('load', ...) 初始化占位——它对已缓存图片可能不触发img 默认带 class="img-loading",JS 启动时立即检查 naturalWidth > 0 或 complete 属性Image 对象,判断逻辑需降级url-loader 或 image-minimizer-webpack-plugin 把小图转 base64,天然规避加载失败真正难的不是加个灰色块,而是让占位行为在缓存、懒加载、服务端渲染、图片跨域各种边界条件下保持一致。很多团队最后发现,最稳的方式是服务端返回带宽高信息的 JSON,前端用它提前渲染容器尺寸,再交由 CSS 控制占位样式——而不是指望某个 JS 库自动搞定一切。