配了三年性能优化,今天才发现网络质量从来不是靠猜的——Network Information API 把这件事从设备层彻底暴露了
做过性能优化的人,最怕的不是代码写得烂,是用户告诉你「页面好卡」,你却复现不了。你本地千兆光纤,测试机全速 4G,结果线上用户说卡——你怎么办?
大多数人的答案是:靠经验猜。视频切到低画质,图片切成 WebP,大文件加个懒加载。靠猜没问题,直到你发现有些用户开着 Data Saver 跑你的「高清首图」,有些人明明在地铁里 3G,你却给他推了 4K 视频。
浏览器其实知道用户网络什么样。只是你从来没问过它。
一行代码,把网络底牌翻出来
navigator.connection,Network Information API,Chrome 38+、Edge 79+ 就支持了。Safari 从来没实现,Firefox 2019 年加过又因为隐私原因移除了,但 Chrome 系和 Electron 应用里基本都能用,全球覆盖率大概 75% 左右。
const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
console.log(conn.effectiveType); // "4g" / "3g" / "2g" / "slow-2g"
console.log(conn.downlink); // 数字,单位 Mbps
console.log(conn.rtt); // 数字,单位毫秒
console.log(conn.saveData); // true / false,用户是否开了省流模式
console.log(conn.type); // "wifi" / "cellular" / "bluetooth" / ...
这五个属性,就是你做自适应加载的全部数据源。
三个最实用的场景
场景一:按网络类型切图片分辨率
这是 Addy Osmani 早在 2017 年就演示过的模式,至今依然是最直接的价值:
function getImageSrc() {
const conn = navigator.connection;
if (!conn) return 'high.jpg';
// 开了省流模式,或者在 2G/3G 下,只给缩略图
if (conn.saveData || conn.effectiveType === 'slow-2g' || conn.effectiveType === '2g') {
return 'low.jpg';
}
// 4G 以上给高清
return 'high.jpg';
}
<img src={getImageSrc()}> 三行代码,比写 Media Query 精确多了——Media Query 只能猜设备,effectiveType 才是真正的网络状态。
场景二:视频延迟加载 + 质量自适应
视频是流量大户,3G 环境下直接加载 1080p 是灾难:
conn.addEventListener('change', () => {
const quality = conn.effectiveType;
if (quality === '4g') {
videoPlayer.src = '1080p.mp4';
videoPlayer.play();
} else if (quality === '3g') {
videoPlayer.src = '480p.mp4';
videoPlayer.play();
} else {
// 2G / slow-2g:只显示封面,不自动播放
videoPlayer.poster = 'poster.jpg';
}
});
监听 change 事件可以捕捉网络切换——用户从 WiFi 切到流量,API 会主动通知,你不需要轮询。
场景三:Data Saver 模式下的降级策略
saveData 属性对应 Chrome 的「节省流量」设置,很多用户主动开启,尤其在移动流量贵的地区:
if (navigator.connection?.saveData) {
// 隐藏视频自动播放
// 改用占位图
// 禁用预加载
document.querySelectorAll('video').forEach(v => {
v.removeAttribute('autoplay');
v.poster = v.dataset.posterFallback;
});
// 图集只加载第一张
lazyImageContainers.forEach((c, i) => {
if (i > 0) c.dataset.src = c.dataset.posterFallback;
});
}
这个场景和 Battery Status API 恰好互补——一个管电量,一个管网速,两个维度叠加才构成完整的「设备感知」。
踩过的三个坑
坑一:effectiveType 不是实时网速,是分类
effectiveType 的值是 "slow-2g"、"2g"、"3g"、"4g",是根据 RTT 和 downlink 估算出来的分类,不是精确测速。用户在 4G 信号边缘来回波动时,可能看到 effectiveType 在 3g 和 4g 之间跳,这是正常行为,别当成 bug。
坑二:Safari 和 Firefox 没有这个 API
写代码前一定要做特性检测:
const conn = navigator.connection;
if (!conn) {
// 浏览器不支持,给默认值走通用逻辑
return 'high.jpg';
}
渐进增强原则:先写不支持时的降级逻辑,再写支持时的优化逻辑。
坑三:downlink 值不是你的下载速度
navigator.connection.downlink 返回的是「有效带宽估算」,是 25kb/s 为粒度取整的值,反映的是当前网络环境能承载的带宽,不是你当前页面的实际下载速度。别拿这个值去做精确的进度条百分比计算。
怎么接入
第一步,先在控制台跑一遍,看你当前网络环境的原始数据:
// 复制到浏览器控制台直接看
setInterval(() => {
const c = navigator.connection;
console.log(`类型:${c.effectiveType} | 带宽:${c.downlink}Mbps | RTT:${c.rtt}ms | 省流:${c.saveData}`);
}, 2000);
第二步,选一个场景落地。推荐从图片自适应开始,ROI 最高——三行代码,换一个 CDN 降费的思路。
第三步,如果你的项目已经接了 Service Worker,在 fetch 事件里加一层网络感知拦截,对不同 effectiveType 返回不同压缩质量的资源,这是最干净的架构做法。
网络质量和电量一样,以前只能靠猜,现在浏览器直接告诉你。配了三年性能优化,下次用户说卡,你可以先问一句:「你网络怎么样?」——然后让代码自己决定怎么处理。
评论区
登录后可评论。