UPTools UPTools
图片 主笔:工具匠

图片转 Base64:什么情况该内联,什么情况别用

前两年我帮一个朋友优化落地页,他特别得意地给我看他的「极致首屏」:CSS 里把 logo、首页 banner、几个分类图标全转成了 Base64 内联进去,HTML 一个外部图片请求都没有。结果 Lighthouse 一跑,FCP 比 Phase 1 还慢了 1.8 秒,LCP 直接飘红。他不信,说不是省了请求吗?我把 HTML 文件指给他看,从原来的 40KB 涨到了 380KB,浏览器光解析这一坨就卡了半天。

这是把 Base64 用反了的典型。图片转 Base64 内联确实能省请求,但它只对几 KB 以内的小图成立。大图一内联,全是负面收益。下面讲清楚这里的取舍。

Data URL 是什么

把图片转成 Base64 后,你会拿到两样东西,长得像这样:

纯 Base64:  iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB...
Data URL:   data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB...

差别就在前面的 data:image/png;base64,。这一段叫 MIME 前缀,告诉浏览器「这是一张 PNG,主体是 Base64 编码」。Data URL 可以直接塞进 <img src="...">,也可以塞进 CSS 的 url(...),浏览器遇到它就读出来渲染,不发任何网络请求。

纯 Base64 没有前缀,单独放进 src 浏览器不认,啥都不显示。这是最常见的坑之一,下面会展开。

小图内联:省一次请求很值

为什么小图适合内联?因为浏览器加载一个页面有请求并发上限,HTTP/1.1 下通常是 6 个,每个外部图片要排队、建连、握手、传一段再关。一张 2KB 的图标走完整套流程,光协议开销可能比图本身还大。把它内联进 HTML,页面下载一次就顺带拿走了,没有额外的往返。

具体哪些图适合内联:

  • 几 KB 以内的图标。logo 缩略图、菜单图标、SVG 装饰图,转完 Base64 也就 3-5KB。
  • 1×1 占位图或像素图。透明占位、灰底占位,原图几十字节,转完也就一百来字节。
  • CSS 背景里的小纹理。比如按钮的渐变底、卡片阴影角,转成 Data URL 塞进 url() 省一次阻塞请求。

判断标准就一条:原图不超过几 KB。这个区间里,Base64 的 33% 膨胀可以接受,省下的请求往返更值。

大图内联:膨胀、阻塞、还缓存不了

大图反过来,内联以后每一项都是负面。

第一,体积膨胀。 Base64 把每 3 字节编成 4 字节,固定膨胀约 33%。一张 200KB 的 JPG banner,转完就是 270KB 左右的字符串。这 270KB 现在全塞进了 HTML 或 CSS,原本可以靠 gzip 压到 30KB 的二进制 JPG,被你变成了无法再压的文本塞进文档。你以为省了一张图,实际上 HTML 涨得比那张图还多。

第二,阻塞解析。 浏览器拿到 HTML 才能解析 DOM;拿到 CSS 才能渲染。把图片塞进这两个文件,等于让图片的体积参与到关键路径里。原本图片可以异步懒加载,现在必须跟着 HTML/CSS 一起下载、一起解析,首屏时间被拖长。我朋友那个案例,banner 内联以后 HTML 从 40KB 涨到 270KB+,浏览器解析这一段就把主线程占满了。

第三,缓存复用没了。 外部图片文件(banner.jpg)是独立的 URL,浏览器会单独缓存它。多个页面用到同一张图,只下载一次;图片没变就一直走缓存,响应头 cache-control 完全生效。一旦塞进 HTML,缓存粒度就变成了「整个 HTML 文件」:HTML 一改,所有内联的图跟着一起重新下载。跨页面共用也别想了。

第四,CDN 和图片优化链路失效。 外部图片可以走 CDN 做格式转换、尺寸裁剪、WebP/AVIF 协商。Base64 字符串进了 HTML 就锁死了,想换个格式得改源码重新发版。

简单一句话:大图一律走外部文件,别内联。常见的边界是 4-8KB,超过这个数就别考虑 Base64 了,老老实实写成 <img src="/banner.webp">

纯 Base64 没前缀的坑

回到开头那个坑。你在工具里转完 Base64,复制出来的可能是纯字符串(iVBORw0KGgo...),也可能是 Data URL(data:image/png;base64,iVBORw0KGgo...)。两者不能混用:

  • 放进 <img src>:必须用 Data URL。纯 Base64 丢进去,浏览器不知道按什么 MIME 解,啥都不显示,连报错都不明显。
  • 放进 CSS url():同样要 Data URL。
  • 放进 JS 或 JSON 里传给后端:这时候通常用纯 Base64,前缀自己根据业务约定补。

排查「我转了 Base64 但图不显示」的思路,第一步永远是看一眼 src 里有没有 data:image/...;base64, 这个前缀。没有就补上,对应格式写对(PNG 写 image/png,JPG 写 image/jpeg,注意 JPEG 不是 JPG)。

实操

要把一张图标转成 Data URL,或者反过来核对你接口里收到的那串 Base64 到底是什么图,可以丢进 UPTools 图片 Base64 互转工具。它同时输出 Data URL 和纯 Base64 两种格式,也支持把 Base64 还原成图片预览,看一眼就能确认编码对不对。本地处理,不上传。

三句话总结

小图(几 KB 以内的图标、占位图、CSS 纹理)内联进 HTML/CSS 能省一次请求,值得。大图别内联,33% 膨胀 + 阻塞 HTML/CSS 解析 + 无法单独缓存 + 失去 CDN 优化,收益全负。转出来的 Base64 分清楚纯字符串和 Data URL,放进 src 必须带 data:image/...;base64, 前缀,否则图不显示。

相关文章