最直接、兼容性最好、最可控的 background-image 适配方式是 css 媒体查询分断点切换:移动优先顺序声明,小屏规则在前、大屏在后;路径用真实可访问的相对或绝对 url;禁用 transition: background-image,改用双伪元素 opacity 过渡实现淡入效果。

用 CSS 媒体查询分断点切换 background-image,是最直接、兼容性最好、也最可控的方式。它不依赖 JS,不预加载所有图(现代浏览器会按需下载当前匹配的资源),还能精准适配不同构图——比如移动端用横版裁切、桌面端用竖版全景。
按屏幕宽度写媒体查询,从小到大顺序声明
浏览器按 CSS 规则的顺序覆盖,后写的规则优先生效。所以必须把小屏规则放前面,大屏放后面:
-
错误写法:先写
@media (min-width: 1200px),再写@media (max-width: 768px)→ 小屏可能被大屏规则覆盖 -
正确写法:从移动优先开始,依次是
@media (max-width: 767px)→@media (min-width: 768px) and (max-width: 1199px)→@media (min-width: 1200px) - 每个断点内只改
background-image,其他如background-size: cover、background-position: center保持统一,避免切换时跳变
图片路径要真实可访问,别用构建别名
CSS 文件里不解析 Webpack 的 @/assets 或 Vite 的 $lib 这类别名。路径必须是最终能被浏览器请求到的相对或绝对 URL:
- ✅ 可以:
url("../images/banner-mobile.webp")、url("/static/banner-desktop.jpg") - ❌ 不行:
url("@/assets/banner.webp")、url("$lib/images/bg.svg")→ 浏览器 404 - 建议所有背景图都放在
/public或/static目录下,确保静态可访问
需要平滑淡入?别动 background-image,用双伪元素
transition: background-image 是无效的,规范禁止插值,Chrome 静默忽略,Safari/Firefox 行为不一致。可靠做法是:
- 容器设固定高度(如
height: 400px),不能是height: auto - 用
::before和::after各放一张图,都position: absolute覆盖同一区域 - 初始状态:主图 opacity: 1,备图 opacity: 0;切换时同时过渡两者 opacity
- 提前预加载备图:
::after { content: url("banner-desktop.jpg"); visibility: hidden; }
别指望 image-set() 做分辨率适配
image-set() 只认设备像素比(DPR),不响应视口宽度。iPad Pro(2x)永远走 2x 图,1080p 笔记本(1x)拿不到高清图——这不是 bug,是设计限制。Firefox 还完全不支持它用于 background-image。真要加 DPR 适配,只能在每个媒体查询块里叠加写,例如:
@media (min-width: 1200px) { .hero { background-image: image-set("banner-2x.jpg" 2x, "banner-1x.jpg" 1x); } }- 但得接受 Firefox 下回退到 1x 图,且无法解决“小屏加载小图”这个核心需求











