结论是不能靠猜网络类型,浏览器不暴露4G/WiFi状态;真正可用的是connection.effectiveType与picture/srcset组合加JS动态降级策略。

直接说结论:不能靠猜网络类型,浏览器不暴露 4G/WiFi 状态;真正能用的,是 connection.effectiveType + picture/srcset 组合,再配合 JS 动态降级策略。
为什么不能用 navigator.onLine 或 network type 判断
很多开发者第一反应是监听 navigator.onLine,但它只反映「是否连上网络」,不是「网速快慢」;而 navigator.connection.type 在 Chrome 和 Edge 中已被废弃,Safari 从未支持,Firefox 仅返回 unknown。实际测试中,navigator.connection.effectiveType 是唯一稳定可用的信号,它返回 'slow-2g'、'2g'、'3g'、'4g' 四种值(注意:没有 'wifi')。
-
effectiveType基于 RTT 和下行带宽估算,由浏览器主动维护,无需权限 - 它默认每 10 秒更新一次,可监听
change事件响应变化 - 首次访问时可能为
undefined,需 fallback 到默认质量
用 picture + srcset 实现图片自动适配
静态资源层面,picture 元素才是应对网络波动的主力,srcset 单独用只能解决分辨率/像素密度问题,无法按网速选图。
- 必须用
media属性结合effectiveType的值做条件匹配——但注意:picture的media不支持 JS 表达式,所以得靠 JS 动态切换source的srcset - 推荐结构:一个
picture容器,内含多个source,每个source对应一种effectiveType,并用data-effective-type标记,JS 根据当前值激活对应source -
img标签的src必须保留,作为所有浏览器都支持的兜底 fallback
示例:
立即学习“前端免费学习笔记(深入)”;
<picture id="hero-img"> <source data-effective-type="slow-2g" srcset="hero-200w.webp 200w, hero-400w.webp 400w"> <source data-effective-type="2g" srcset="hero-400w.webp 400w, hero-800w.webp 800w"> <source data-effective-type="3g" srcset="hero-800w.webp 800w, hero-1200w.webp 1200w"> <source data-effective-type="4g" srcset="hero-1200w.webp 1200w, hero-1920w.webp 1920w"> <img src="hero-fallback.jpg" alt="Hero"> </picture>
JS 动态切换 + 首屏文本保底
光靠 HTML 结构不够,必须用 JS 拿到 effectiveType 后主动切换 source 并触发重新加载。这里容易踩三个坑:
- 不能等 DOMContentLoaded 再执行——弱网下这个事件可能延迟数秒,首屏图片已错失最佳加载时机
- 切换
source.srcset后,必须手动调用img.src = img.src强制浏览器重新解析(否则旧缓存会继续用) - 首屏文本元素(
<h1>、<p>)必须写死在 HTML 最前面,且带内联font-size和color,否则 JS 还没跑完,用户看到的就是白屏或骨架屏
关键代码片段:
const pic = document.getElementById('hero-img');
const img = pic.querySelector('img');
const sources = pic.querySelectorAll('source');
function updateImageByNetwork() {
const type = navigator.connection?.effectiveType || '4g';
const targetSource = Array.from(sources).find(s => s.dataset.effectiveType === type);
if (targetSource) {
img.srcset = targetSource.srcset;
img.src = img.src; // 触发重载
}
}
// 页面加载早期就执行,不等 DOMContentLoaded
if ('connection' in navigator && 'effectiveType' in navigator.connection) {
updateImageByNetwork();
navigator.connection.addEventListener('change', updateImageByNetwork);
}
字体和脚本的 fetchpriority 要配对使用
图片适配只是局部,整个组件加载链路里,字体和关键脚本也得按网络状况分级调度。但很多人只设 fetchpriority="high",却忘了配 loading="eager" —— 单独设 high 会被其他高优请求挤掉,尤其当页面同时 preload 了 hero 图和字体时。
- 首屏字体文件必须加
crossorigin,否则 CORS 失败导致 fallback 字体撑开布局 - 非关键脚本(如埋点、广告 SDK)统一设
fetchpriority="low",否则会抢文本通道带宽 -
fetchpriority只对当前导航中「确定马上要用」的资源有效,不要给整页所有link都加
容易被忽略的是:弱网探测后注入的降级样式,绝不能写进外部 CSS 文件里——SSR 会一并输出,污染 SEO;也不能等 CSS 超时再 fallback,弱网下外部 CSS pending 超 2 秒已是常态,用户早关页了。



















