1x1 透明 GIF 埋点请求
为什么数据埋点常用 1x1 像素透明 GIF 图片
问题
为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 GIF 图片?
解答
1x1 GIF 埋点的优势
使用 1x1 透明 GIF 做埋点是一种经典方案,主要有以下原因:
体积极小
// 1x1 透明 GIF 的 Base64 编码,仅 43 字节
R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
相比 PNG(67 字节)和 BMP(数百字节),GIF 是最小的。
天然跨域
// img 标签不受同源策略限制
const img = new Image();
img.src = 'https://analytics.example.com/collect?event=click&id=123';
不需要服务端配置 CORS,任何域名都能发送。
不阻塞页面
// 图片加载是异步的,不会阻塞页面渲染和 JS 执行
function track(data) {
const img = new Image();
const params = new URLSearchParams(data).toString();
img.src = `https://analytics.example.com/collect?${params}`;
// 无需等待响应,发出去就行
}
track({ event: 'pageview', page: '/home' });
页面卸载时更可靠
// 页面关闭时,img 请求比 XHR 更可能发送成功
window.addEventListener('beforeunload', () => {
// XHR 可能被取消
// img 请求通常能发出去
new Image().src = 'https://analytics.example.com/collect?event=leave';
});
无需处理响应
// 服务端返回 1x1 GIF,客户端不需要任何处理
// 不像 XHR 需要处理 readyState、status 等
function sendBeacon(url, data) {
const img = new Image();
img.src = `${url}?${new URLSearchParams(data)}`;
// 完事,不用管了
}
完整的埋点实现
class Tracker {
constructor(endpoint) {
this.endpoint = endpoint;
this.queue = [];
}
// 发送单个埋点
send(data) {
const img = new Image();
const params = new URLSearchParams({
...data,
t: Date.now(), // 防缓存
}).toString();
img.src = `${this.endpoint}?${params}`;
}
// 页面卸载时发送(现代方案)
sendOnUnload(data) {
const params = new URLSearchParams(data).toString();
const url = `${this.endpoint}?${params}`;
// 优先使用 sendBeacon(更可靠)
if (navigator.sendBeacon) {
navigator.sendBeacon(url);
} else {
// 降级到 img
new Image().src = url;
}
}
}
// 使用
const tracker = new Tracker('https://analytics.example.com/collect');
tracker.send({ event: 'click', button: 'submit' });
现代替代方案
// 1. Navigator.sendBeacon - 专为埋点设计
navigator.sendBeacon('/collect', JSON.stringify({ event: 'click' }));
// 2. fetch + keepalive - 页面卸载时也能发送
fetch('/collect', {
method: 'POST',
body: JSON.stringify({ event: 'click' }),
keepalive: true, // 关键:允许请求在页面卸载后继续
});
关键点
- 体积最小:1x1 GIF 仅 43 字节,比 PNG、BMP 都小
- 跨域无障碍:img 标签不受同源策略限制
- 不阻塞渲染:异步加载,不影响页面性能
- 卸载时可靠:比 XHR 更容易在页面关闭前发出
- 实现简单:不需要处理响应,new Image() 一行搞定
- 现代替代:
sendBeacon和fetch + keepalive是更好的选择
目录