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 多路复用是最佳方案,一个连接可处理所有请求
目录