直接批量替换 src 为 webp 会导致白屏,因旧浏览器不支持 webp 且无降级机制,且带参数的 url 可能失效;必须用 + type 属性显式声明格式链,并以 为必需兜底。

为什么直接批量改 src 会出问题
很多人想用正则或文本替换工具全局把 jpg 换成 webp,结果上线后一堆白屏。根本原因是:浏览器不认识 webp 时不会自动 fallback,也不会报错,只留空 alt 文字。Safari 13.1 之前、IE、旧安卓 WebView 都这样。更糟的是,如果原始路径本身带查询参数(比如 src="logo.jpg?ver=2"),直接替换后可能变成 logo.webp?ver=2——而 CDN 根本不托管带参数的 WebP 文件。
必须用 <picture></picture> + type 属性做格式链降级
原生降级唯一可靠方式是显式声明支持链,不能靠 JS 或服务器 UA 判断。关键点不是“有没有 <source></source>”,而是结构是否合规:
-
<source></source>必须带type属性,例如type="image/avif";没写type的 Safari 直接跳过 - 顺序必须从现代到老旧:
<source type="image/avif"></source>→<source type="image/webp"></source>→ 最后是<img src="fallback.jpg" alt="..."> -
<img>不是可选配,是必需兜底;它的src必须指向一个真实可加载的静态资源(如 CDN 上的/img/logo-fallback.jpg) - 所有
src和srcset中的路径必须是相对路径且以 HTML 文件所在目录为基准,混用./images/和images/在嵌套路由下必挂
用 AST 解析器批量生成 <picture></picture> 结构
手写几百个 <picture></picture> 不现实,但用正则替换又容易破坏 HTML 结构(比如误改注释、JS 字符串里的 src)。真正安全的做法是解析 AST:
- 用
parse5(符合 HTML5 标准)读取文档,遍历所有img节点 - 提取原
src、alt、class、data-*属性,保留语义 - 按规则生成
<picture></picture>:每个<source></source>对应一个现代格式 URL(如把logo.jpg替换为logo.avif),末尾插入原img作为兜底 - 特别注意:不要在循环里反复调用
appendChild,全部先塞进DocumentFragment再一次性挂载,避免重排卡顿
开发阶段必须监听 error 事件定位失效图
光靠结构合规还不够。部署后图片仍可能因路径错、CDN 挂、文件被删而失效。手动查太慢,得自动化:
- 全局注册一次:
document.querySelectorAll('img').forEach(img => img.addEventListener('error', e => { e.target.src = '/assets/placeholder.svg'; })) - 开发时快速排查:
Array.from(document.images).filter(i => i.naturalWidth === 0)直接列出所有加载失败的img - 别用
onerror内联写法(如<img onerror="...">),每次错误都要重新解析字符串,逻辑无法复用 - 更稳妥的内联写法:
onerror="if (!this.src.includes('placeholder')) this.src='/assets/placeholder.svg'; this.onerror=null;",防死循环
最容易被忽略的是:所有降级都依赖 img 的 src 始终可用。如果 SSR 渲染时这个值是空、动态生成的、或被 CSS 隐藏了,整个链就断了——连占位图都出不来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











