HTTP/1 多图片加载优化

解决 HTTP/1.1 下多图片并发加载受限的问题

问题

页面有 10 张图片,在 HTTP/1.1 下是怎样的加载表现?如何优化?

解答

HTTP/1.1 的加载表现

浏览器对同一域名的并发连接数有限制,通常是 6 个。10 张图片会分两批加载:

第一批:6 张图片并行加载
第二批:等第一批完成后,剩余 4 张开始加载

在 Chrome DevTools 的 Network 面板可以看到瀑布图呈现阶梯状。

解决方案

1. 域名分片(Domain Sharding)

将图片分散到多个子域名,突破单域名连接限制:

<!-- 分散到不同子域名 -->
<img src="https://img1.example.com/a.png">
<img src="https://img2.example.com/b.png">
<img src="https://img3.example.com/c.png">
// 动态分配域名
function getImageUrl(filename, index) {
  const domains = ['img1', 'img2', 'img3'];
  const domain = domains[index % domains.length];
  return `https://${domain}.example.com/${filename}`;
}

2. 雪碧图(CSS Sprites)

将多张小图合并成一张大图:

.icon {
  width: 32px;
  height: 32px;
  background-image: url('sprites.png');
}

.icon-home { background-position: 0 0; }
.icon-user { background-position: -32px 0; }
.icon-cart { background-position: -64px 0; }

3. Base64 内联

小图片直接内联到 CSS 或 HTML 中:

.logo {
  /* 省去一次 HTTP 请求 */
  background-image: url('data:image/png;base64,iVBORw0KGgo...');
}

4. 图片懒加载

只加载可视区域的图片:

<img src="placeholder.png" data-src="real-image.png" loading="lazy">
// 原生懒加载(现代浏览器)
<img src="image.png" loading="lazy">

// IntersectionObserver 实现
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img);
});

5. 升级到 HTTP/2

HTTP/2 支持多路复用,一个连接可并行传输多个请求:

# Nginx 配置启用 HTTP/2
server {
    listen 443 ssl http2;
    # ...
}

各方案对比

方案优点缺点
域名分片简单有效增加 DNS 查询,HTTP/2 下反而有害
雪碧图减少请求数维护麻烦,单图更新需重新生成
Base64零请求增大文件体积约 33%,无法缓存
懒加载减少首屏请求滚动时可能出现空白
HTTP/2根本解决问题需要 HTTPS

关键点

  • HTTP/1.1 同域名并发限制通常为 6 个连接
  • 域名分片在 HTTP/1.1 有效,但 HTTP/2 下应避免使用
  • 雪碧图适合小图标,不适合频繁更新的图片
  • Base64 适合小于 2KB 的图片
  • HTTP/2 多路复用是最佳方案,一个连接可处理所有请求