pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:Network Information API - 完整教程与代码示例
一、教程简介
Network Information API 允许 Web 应用获取设备的网络连接信息,包括连接类型(WiFi、蜂窝网络等)、有效带宽、往返时间等。通过这些信息,开发者可以根据网络状况动态调整应用行为,例如在慢速网络下加载低分辨率图片、减少数据请求、预缓存关键资源等,从而优化用户体验。
Network Information API 对于移动端应用尤为重要,因为移动设备的网络环境变化频繁,从高速 WiFi 到慢速 2G/3G 网络的切换随时可能发生。通过感知网络状态变化,应用可以做出智能的适配决策。
二、核心概念
1. NetworkInformation 对象
NetworkInformation 是 Network Information API 的核心接口,通过 navigator.connection 属性获取。
代码示例
// 获取 NetworkInformation 对象
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection) {
console.log(connection.effectiveType); // 有效连接类型
console.log(connection.downlink); // 下行带宽估算(Mbps)
console.log(connection.rtt); // 往返时间估算(ms)
console.log(connection.saveData); // 是否启用省流模式
console.log(connection.type); // 连接类型(已废弃)
}2. effectiveType(有效连接类型)
代码示例
// effectiveType 返回以下值之一:
// 'slow-2g' - 极慢网络(RTT > 2000ms,下行 < 50kbps)
// '2g' - 慢速网络(RTT > 1000ms,下行 < 70kbps)
// '3g' - 中速网络(RTT > 250ms,下行 < 700kbps)
// '4g' - 快速网络(RTT < 250ms,下行 > 700kbps)
const connection = navigator.connection;
if (connection) {
switch (connection.effectiveType) {
case 'slow-2g':
console.log('极慢网络,仅加载文本');
break;
case '2g':
console.log('慢速网络,加载低分辨率资源');
break;
case '3g':
console.log('中速网络,加载中等质量资源');
break;
case '4g':
console.log('快速网络,加载高质量资源');
break;
}
}3. downlink(下行带宽)
代码示例
// downlink 返回估算的下行带宽,单位为 Mbps
// 值基于最近活跃连接的加权平均值
// 注意:这是估算值,不是精确测量
const connection = navigator.connection;
if (connection) {
console.log('估算下行带宽:', connection.downlink, 'Mbps');
// 根据带宽选择资源质量
if (connection.downlink >= 5) {
loadHighQualityVideo();
} else if (connection.downlink >= 1) {
loadMediumQualityVideo();
} else {
loadLowQualityVideo();
}
}4. rtt(往返时间)
代码示例
// rtt 返回估算的往返时间,单位为毫秒
// 基于最近活跃连接的加权平均值
const connection = navigator.connection;
if (connection) {
console.log('估算 RTT:', connection.rtt, 'ms');
// 根据 RTT 调整超时设置
const timeout = Math.max(connection.rtt * 3, 3000);
console.log('建议超时:', timeout, 'ms');
}5. saveData(省流模式)
代码示例
// saveData 返回布尔值,表示用户是否启用了省流模式
// 当用户在浏览器设置中启用"省流"或"数据节省"模式时为 true
const connection = navigator.connection;
if (connection) {
if (connection.saveData) {
console.log('用户启用了省流模式,减少数据传输');
// 减少请求、加载低分辨率资源、禁用自动播放等
}
}6. change 事件
代码示例
// 监听网络状态变化
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection) {
connection.addEventListener('change', () => {
console.log('网络状态变化:');
console.log('有效类型:', connection.effectiveType);
console.log('下行带宽:', connection.downlink, 'Mbps');
console.log('RTT:', connection.rtt, 'ms');
console.log('省流模式:', connection.saveData);
});
}三、语法与用法
获取连接信息
代码示例
// 标准获取方式
const connection = navigator.connection;
// 兼容性获取方式
const connection = navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection;
// 检测支持
if (connection) {
// Network Information API 可用
} else {
// 不可用,使用默认策略
}读取属性
代码示例
function getConnectionInfo() {
const connection = navigator.connection;
if (!connection) return null;
return {
effectiveType: connection.effectiveType, // 'slow-2g' | '2g' | '3g' | '4g'
downlink: connection.downlink, // Mbps
rtt: connection.rtt, // ms
saveData: connection.saveData, // boolean
type: connection.type, // 已废弃,部分浏览器仍可用
downlinkMax: connection.downlinkMax // 已废弃
};
}监听变化
代码示例
function onNetworkChange(callback) {
const connection = navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection;
if (!connection) return;
const handler = () => {
callback({
effectiveType: connection.effectiveType,
downlink: connection.downlink,
rtt: connection.rtt,
saveData: connection.saveData
});
};
connection.addEventListener('change', handler);
// 返回取消监听函数
return () => {
connection.removeEventListener('change', handler);
};
}四、代码示例
示例1:网络状态监控面板
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Network Information API - 网络状态监控面板</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #0c0c1d, #1a1a3e);
color: #e0e0e0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.panel {
background: rgba(255,255,255,0.05);
border-radius: 20px;
padding: 30px;
width: 500px;
max-width: 100%;
backdrop-filter: blur(10px);
}
h1 { text-align: center; color: #00cec9; margin-bottom: 25px; }
.network-type { text-align: center; margin-bottom: 25px; }
.type-badge {
display: inline-block;
padding: 10px 30px;
border-radius: 25px;
font-size: 24px;
font-weight: bold;
transition: all 0.5s;
}
.type-badge.slow-2g { background: rgba(214,48,49,0.2); color: #d63031; border: 2px solid #d63031; }
.type-badge.2g { background: rgba(225,112,85,0.2); color: #e17055; border: 2px solid #e17055; }
.type-badge.3g { background: rgba(253,203,110,0.2); color: #fdcb6e; border: 2px solid #fdcb6e; }
.type-badge.4g { background: rgba(0,206,201,0.2); color: #00cec9; border: 2px solid #00cec9; }
.type-badge.unknown { background: rgba(99,110,114,0.2); color: #636e72; border: 2px solid #636e72; }
.metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 25px; }
.metric-card { background: rgba(255,255,255,0.05); border-radius: 12px; padding: 18px; text-align: center; }
.metric-card .label { font-size: 12px; color: #888; margin-bottom: 8px; }
.metric-card .value { font-size: 28px; font-weight: bold; color: #00cec9; }
.metric-card .unit { font-size: 12px; color: #636e72; }
</style>
</head>
<body>
<div class="panel">
<h1>Network Monitor</h1>
<div class="network-type">
<div class="type-badge unknown" id="typeBadge">检测中</div>
</div>
<div class="metrics">
<div class="metric-card">
<div class="label">下行带宽</div>
<div class="value" id="downlinkValue">--</div>
<div class="unit">Mbps</div>
</div>
<div class="metric-card">
<div class="label">往返时间</div>
<div class="value" id="rttValue">--</div>
<div class="unit">ms</div>
</div>
</div>
</div>
<script>
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection) {
function updateUI() {
const type = connection.effectiveType || 'unknown';
document.getElementById('typeBadge').textContent = type.toUpperCase();
document.getElementById('typeBadge').className = 'type-badge ' + type;
document.getElementById('downlinkValue').textContent = connection.downlink.toFixed(1);
document.getElementById('rttValue').textContent = connection.rtt;
}
updateUI();
connection.addEventListener('change', updateUI);
}
</script>
</body>
</html>示例2:自适应图片加载
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Network Information API - 自适应图片加载</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #1e272e; color: #d2dae2; min-height: 100vh; padding: 20px; }
.container { max-width: 700px; margin: 0 auto; }
h1 { color: #0fbcf9; text-align: center; margin-bottom: 20px; }
.network-info { display: flex; align-items: center; gap: 15px; padding: 15px 20px; background: rgba(255,255,255,0.05); border-radius: 12px; margin-bottom: 25px; }
.net-type { padding: 6px 16px; border-radius: 15px; font-size: 14px; font-weight: bold; }
.net-type.4g { background: rgba(0,206,201,0.2); color: #00cec9; }
.net-type.3g { background: rgba(253,203,110,0.2); color: #fdcb6e; }
.net-type.2g { background: rgba(225,112,85,0.2); color: #e17055; }
.net-type.slow-2g { background: rgba(214,48,49,0.2); color: #d63031; }
.image-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.image-card { border-radius: 12px; overflow: hidden; position: relative; aspect-ratio: 1; background: rgba(255,255,255,0.05); }
.image-card img { width: 100%; height: 100%; object-fit: cover; }
</style>
</head>
<body>
<div class="container">
<h1>Adaptive Image Loading</h1>
<div class="network-info">
<span class="net-type" id="netType">检测中</span>
<span id="netDetail">正在检测网络状态...</span>
</div>
<div class="image-grid" id="imageGrid"></div>
</div>
<script>
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
const images = [{seed:'mountain'},{seed:'ocean'},{seed:'forest'},{seed:'city'},{seed:'sunset'},{seed:'flower'}];
function getQuality() {
if (!connection) return 'sd';
if (connection.saveData) return 'ld';
switch (connection.effectiveType) {
case '4g': return 'hd';
case '3g': return 'sd';
case '2g': return 'ld';
case 'slow-2g': return 'placeholder';
default: return 'sd';
}
}
function loadImages() {
const quality = getQuality();
const grid = document.getElementById('imageGrid');
grid.innerHTML = '';
images.forEach(img => {
const card = document.createElement('div');
card.className = 'image-card';
const sizes = {hd: 800, sd: 400, ld: 200, placeholder: 50};
const size = sizes[quality] || sizes.sd;
card.innerHTML = `<img src="https://picsum.photos/seed/${img.seed}/${size}/${size}.jpg" loading="lazy">`;
grid.appendChild(card);
});
}
loadImages();
if (connection) connection.addEventListener('change', loadImages);
</script>
</body>
</html>示例3:网络感知的数据请求策略
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Network Information API - 网络感知数据请求</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0a0a1a; color: #e0e0e0; min-height: 100vh; padding: 20px; }
.container { max-width: 650px; margin: 0 auto; }
h1 { color: #a29bfe; text-align: center; margin-bottom: 20px; }
.status-bar { display: flex; align-items: center; gap: 12px; padding: 12px 20px; background: rgba(255,255,255,0.05); border-radius: 12px; margin-bottom: 20px; }
.status-dot { width: 10px; height: 10px; border-radius: 50%; background: #00b894; }
.status-dot.slow { background: #fdcb6e; }
.btn { padding: 8px 18px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.05); color: #d2dae2; cursor: pointer; font-size: 13px; }
.btn.primary { background: rgba(162,155,254,0.2); border-color: #a29bfe; color: #a29bfe; }
.log-area { background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px; max-height: 250px; overflow-y: auto; font-family: monospace; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<h1>Network-Aware Requests</h1>
<div class="status-bar">
<div class="status-dot" id="statusDot"></div>
<span id="statusText">检测网络状态...</span>
</div>
<button class="btn primary" id="fetchBtn">发送请求</button>
<div class="log-area" id="logArea"></div>
</div>
<script>
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
const strategies = {
'4g': {timeout: 5000, retries: 2},
'3g': {timeout: 10000, retries: 3},
'2g': {timeout: 20000, retries: 4},
'slow-2g': {timeout: 30000, retries: 5}
};
function getStrategy() {
if (!connection) return {timeout: 10000, retries: 3};
return strategies[connection.effectiveType] || strategies['3g'];
}
document.getElementById('fetchBtn').addEventListener('click', async () => {
const strategy = getStrategy();
const controller = new AbortController();
setTimeout(() => controller.abort(), strategy.timeout);
try {
const response = await fetch('https://httpbin.org/get', {signal: controller.signal});
document.getElementById('logArea').innerHTML += '<div style="color:#00b894">请求成功</div>';
} catch (e) {
document.getElementById('logArea').innerHTML += '<div style="color:#d63031">请求失败: ' + e.message + '</div>';
}
});
</script>
</body>
</html>示例4:在线/离线状态与网络质量综合监控
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Network Information API - 综合网络监控</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0f0f23; color: #e0e0e0; min-height: 100vh; padding: 20px; }
.container { max-width: 700px; margin: 0 auto; }
h1 { color: #e056fd; text-align: center; margin-bottom: 25px; }
.online-status { text-align: center; padding: 15px; border-radius: 12px; margin-bottom: 20px; font-size: 16px; font-weight: bold; }
.online-status.online { background: rgba(0,184,148,0.15); border: 1px solid rgba(0,184,148,0.3); color: #00b894; }
.online-status.offline { background: rgba(214,48,49,0.15); border: 1px solid rgba(214,48,49,0.3); color: #d63031; }
.dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
.dash-card { background: rgba(255,255,255,0.05); border-radius: 12px; padding: 18px; text-align: center; }
.dash-card .label { font-size: 12px; color: #808e9b; margin-bottom: 5px; }
.dash-card .value { font-size: 22px; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1>Network Dashboard</h1>
<div class="online-status online" id="onlineStatus">在线</div>
<div class="dashboard">
<div class="dash-card">
<div class="label">连接类型</div>
<div class="value" id="dType">--</div>
</div>
<div class="dash-card">
<div class="label">下行带宽</div>
<div class="value" id="dDownlink">-- Mbps</div>
</div>
<div class="dash-card">
<div class="label">往返延迟</div>
<div class="value" id="dRtt">-- ms</div>
</div>
<div class="dash-card">
<div class="label">省流模式</div>
<div class="value" id="dSaveData">--</div>
</div>
</div>
</div>
<script>
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
function updateDashboard() {
const isOnline = navigator.onLine;
const statusEl = document.getElementById('onlineStatus');
statusEl.textContent = isOnline ? '在线' : '离线';
statusEl.className = 'online-status ' + (isOnline ? 'online' : 'offline');
if (connection) {
document.getElementById('dType').textContent = (connection.effectiveType || 'unknown').toUpperCase();
document.getElementById('dDownlink').textContent = connection.downlink.toFixed(1) + ' Mbps';
document.getElementById('dRtt').textContent = connection.rtt + ' ms';
document.getElementById('dSaveData').textContent = connection.saveData ? '已启用' : '未启用';
}
}
updateDashboard();
if (connection) connection.addEventListener('change', updateDashboard);
window.addEventListener('online', updateDashboard);
window.addEventListener('offline', updateDashboard);
</script>
</body>
</html>五、浏览器兼容性
兼容性注意事项
代码示例
// 1. Firefox 和 Safari 不支持 Network Information API
// 需要提供降级方案
// 2. 完整的兼容性检测
function isNetworkInfoSupported() {
return !!(navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection);
}
// 3. 安全获取连接信息
function getConnection() {
return navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection ||
null;
}
// 4. iOS 和 Firefox 的降级方案
// 可以使用 Performance API 估算网络质量
async function estimateNetworkQuality() {
if (getConnection()) {
return {
type: getConnection().effectiveType,
downlink: getConnection().downlink,
rtt: getConnection().rtt
};
}
// 降级:使用 Resource Timing API 估算
const entries = performance.getEntriesByType('resource');
if (entries.length > 0) {
const recent = entries.slice(-5);
const avgRtt = recent.reduce((sum, e) => sum + (e.responseEnd - e.requestStart), 0) / recent.length;
return {
type: avgRtt < 250 ? '4g' : avgRtt < 1000 ? '3g' : '2g',
downlink: null,
rtt: Math.round(avgRtt)
};
}
return { type: 'unknown', downlink: null, rtt: null };
}六、注意事项与最佳实践
1. 渐进增强策略
代码示例
// 最佳实践:Network Information API 作为增强,而非依赖
// 基础功能:所有用户都能使用
function loadImage(baseUrl) {
return fetch(baseUrl);
}
// 增强功能:根据网络状态优化
function loadAdaptiveImage(baseUrl) {
const connection = getConnection();
if (!connection) {
// API 不可用,使用默认质量
return loadImage(baseUrl);
}
// 根据网络状态选择质量
let quality = 'medium';
if (connection.saveData) {
quality = 'low';
} else {
switch (connection.effectiveType) {
case '4g': quality = 'high'; break;
case '3g': quality = 'medium'; break;
case '2g':
case 'slow-2g': quality = 'low'; break;
}
}
const url = `${baseUrl}?quality=${quality}`;
return loadImage(url);
}2. 避免频繁切换
代码示例
// 问题:网络状态频繁变化导致频繁切换策略
// 解决方案:添加防抖和最小切换间隔
class StableNetworkStrategy {
constructor(minSwitchInterval = 30000) {
this.minSwitchInterval = minSwitchInterval;
this.lastSwitchTime = 0;
this.currentStrategy = 'default';
}
getStrategy() {
const connection = getConnection();
if (!connection) return 'default';
const now = Date.now();
if (now - this.lastSwitchTime < this.minSwitchInterval) {
return this.currentStrategy;
}
let newStrategy;
switch (connection.effectiveType) {
case '4g': newStrategy = 'high'; break;
case '3g': newStrategy = 'medium'; break;
case '2g':
case 'slow-2g': newStrategy = 'low'; break;
default: newStrategy = 'default';
}
if (newStrategy !== this.currentStrategy) {
this.currentStrategy = newStrategy;
this.lastSwitchTime = now;
}
return this.currentStrategy;
}
}3. 结合 Service Worker
代码示例
// Network Information API 与 Service Worker 结合使用
// 在 Service Worker 中根据网络状态选择缓存策略
// service-worker.js
self.addEventListener('fetch', (event) => {
const connection = self.navigator.connection;
if (connection) {
switch (connection.effectiveType) {
case '4g':
// 高速网络:网络优先
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
break;
case '3g':
// 中速网络:缓存优先,后台更新
event.respondWith(
caches.match(event.request).then(cached => {
const fetchPromise = fetch(event.request).then(response => {
const cache = await caches.open('dynamic');
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
})
);
break;
case '2g':
case 'slow-2g':
// 慢速网络:仅缓存
event.respondWith(
caches.match(event.request)
);
break;
default:
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
}
} else {
// API 不可用,使用默认策略
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
}
});4. 隐私考虑
代码示例
// Network Information API 可能被用于指纹追踪
// 最佳实践:降低精度,不发送到服务器
// 不好的做法:将精确的网络信息发送到服务器
// fetch('/analytics', {
// method: 'POST',
// body: JSON.stringify({
// type: connection.effectiveType,
// downlink: connection.downlink, // 精确值
// rtt: connection.rtt // 精确值
// })
// });
// 好的做法:仅发送粗略信息
function getNetworkCategory() {
const connection = getConnection();
if (!connection) return 'unknown';
// 只返回大类,不返回精确值
if (connection.saveData) return 'saving';
if (connection.effectiveType === '4g') return 'fast';
if (connection.effectiveType === '3g') return 'medium';
return 'slow';
}七、代码规范示例
完整的网络感知管理封装
代码示例
/**
* NetworkAwareManager - 网络感知管理器
* 封装 Network Information API,提供策略管理、事件监听、优雅降级
*/
class NetworkAwareManager {
// 网络质量等级
static QUALITY = {
FAST: 'fast', // 4G
MEDIUM: 'medium', // 3G
SLOW: 'slow', // 2G
VERY_SLOW: 'very-slow', // slow-2g
OFFLINE: 'offline',
UNKNOWN: 'unknown'
};
// 预定义策略
static STRATEGIES = {
[NetworkAwareManager.QUALITY.FAST]: {
imageQuality: 'high',
requestTimeout: 5000,
retryCount: 2,
concurrency: 6,
cacheMode: 'network-first',
prefetch: true,
autoPlay: true
},
[NetworkAwareManager.QUALITY.MEDIUM]: {
imageQuality: 'medium',
requestTimeout: 10000,
retryCount: 3,
concurrency: 3,
cacheMode: 'stale-while-revalidate',
prefetch: false,
autoPlay: false
},
[NetworkAwareManager.QUALITY.SLOW]: {
imageQuality: 'low',
requestTimeout: 20000,
retryCount: 4,
concurrency: 2,
cacheMode: 'cache-first',
prefetch: false,
autoPlay: false
},
[NetworkAwareManager.QUALITY.VERY_SLOW]: {
imageQuality: 'placeholder',
requestTimeout: 30000,
retryCount: 5,
concurrency: 1,
cacheMode: 'cache-only',
prefetch: false,
autoPlay: false
},
[NetworkAwareManager.QUALITY.OFFLINE]: {
imageQuality: 'placeholder',
requestTimeout: 0,
retryCount: 0,
concurrency: 0,
cacheMode: 'cache-only',
prefetch: false,
autoPlay: false
},
[NetworkAwareManager.QUALITY.UNKNOWN]: {
imageQuality: 'medium',
requestTimeout: 10000,
retryCount: 3,
concurrency: 4,
cacheMode: 'network-first',
prefetch: false,
autoPlay: false
}
};
constructor(options = {}) {
this.connection = navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection;
this.minSwitchInterval = options.minSwitchInterval || 5000;
this.lastSwitchTime = 0;
this.currentQuality = NetworkAwareManager.QUALITY.UNKNOWN;
this.listeners = new Map();
this._initialized = false;
}
/** 初始化 */
async init() {
if (this._initialized) return this;
// 初始评估
this._evaluateNetwork();
// 监听网络变化
if (this.connection) {
this.connection.addEventListener('change', () => this._onNetworkChange());
}
// 监听在线/离线
window.addEventListener('online', () => this._onNetworkChange());
window.addEventListener('offline', () => this._onNetworkChange());
this._initialized = true;
this._emit('ready', this.getStatus());
return this;
}
/** 评估网络质量 */
_evaluateNetwork() {
if (!navigator.onLine) {
this.currentQuality = NetworkAwareManager.QUALITY.OFFLINE;
return;
}
if (!this.connection) {
this.currentQuality = NetworkAwareManager.QUALITY.UNKNOWN;
return;
}
// 省流模式优先
if (this.connection.saveData) {
this.currentQuality = NetworkAwareManager.QUALITY.SLOW;
return;
}
switch (this.connection.effectiveType) {
case '4g':
this.currentQuality = NetworkAwareManager.QUALITY.FAST;
break;
case '3g':
this.currentQuality = NetworkAwareManager.QUALITY.MEDIUM;
break;
case '2g':
this.currentQuality = NetworkAwareManager.QUALITY.SLOW;
break;
case 'slow-2g':
this.currentQuality = NetworkAwareManager.QUALITY.VERY_SLOW;
break;
default:
this.currentQuality = NetworkAwareManager.QUALITY.UNKNOWN;
}
}
/** 网络变化处理 */
_onNetworkChange() {
const now = Date.now();
if (now - this.lastSwitchTime < this.minSwitchInterval) return;
const oldQuality = this.currentQuality;
this._evaluateNetwork();
if (oldQuality !== this.currentQuality) {
this.lastSwitchTime = now;
this._emit('qualitychange', {
oldQuality,
newQuality: this.currentQuality,
strategy: this.getStrategy()
});
}
this._emit('networkchange', this.getStatus());
}
/** 获取当前策略 */
getStrategy() {
return NetworkAwareManager.STRATEGIES[this.currentQuality] ||
NetworkAwareManager.STRATEGIES[NetworkAwareManager.QUALITY.UNKNOWN];
}
/** 获取当前状态 */
getStatus() {
return {
quality: this.currentQuality,
isOnline: navigator.onLine,
effectiveType: this.connection?.effectiveType || null,
downlink: this.connection?.downlink || null,
rtt: this.connection?.rtt || null,
saveData: this.connection?.saveData || false,
strategy: this.getStrategy()
};
}
/** 判断是否为慢速网络 */
isSlowNetwork() {
return [NetworkAwareManager.QUALITY.SLOW,
NetworkAwareManager.QUALITY.VERY_SLOW,
NetworkAwareManager.QUALITY.OFFLINE].includes(this.currentQuality);
}
/** 判断是否应加载数据 */
shouldLoadData() {
return this.currentQuality !== NetworkAwareManager.QUALITY.OFFLINE;
}
/** 注册事件 */
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return this;
}
/** 触发事件 */
_emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(cb => cb(data));
}
}
/** 销毁 */
destroy() {
this.listeners.clear();
this._initialized = false;
}
}
// 使用示例
const networkManager = new NetworkAwareManager({
minSwitchInterval: 5000
});
networkManager
.on('ready', (status) => {
console.log('网络管理器已启动:', status.quality);
})
.on('qualitychange', ({ oldQuality, newQuality, strategy }) => {
console.log(`网络质量变化: ${oldQuality} -> ${newQuality}`);
console.log('新策略:', strategy);
})
.on('networkchange', (status) => {
console.log('网络状态变化:', status);
});
networkManager.init();八、常见问题与解决方案
常见问题
Firefox 和 Safari 不支持 Network Information API 怎么办?
Firefox 和 Safari 不支持 Network Information API。解决方案是使用 Performance API 的 Resource Timing API 估算网络质量。通过分析最近几个资源的请求和响应时间差,可以估算出 RTT 值,从而判断网络类型(4G/3G/2G)。这种方法虽然不如原生 API 精确,但能在不支持的浏览器上提供基本的网络质量感知能力。
网络状态频繁变化如何处理?
移动设备在 WiFi 和蜂窝网络间频繁切换会导致策略频繁变化。解决方案是使用防抖和滞后策略:设置最小切换间隔(如 5-30 秒),在此间隔内不响应网络变化事件;或者使用延迟处理,在检测到网络变化后等待一段时间再执行策略切换,避免短暂的网络波动导致不必要的调整。
downlink 和 rtt 值不准确怎么办?
downlink 和 rtt 是基于最近活跃连接的估算值,可能与实际网络状况有偏差。解决方案是自行测量网络质量:通过向测试服务器发送小文件请求,使用 Performance API 精确测量请求时间,计算实际的 RTT 和下载速度。多次测量取平均值可以获得更准确的结果。
Network Information API 存在隐私问题吗?
是的,Network Information API 可能被用于指纹追踪。网络类型、带宽、RTT 等组合信息可以作为用户指纹的一部分。最佳实践是降低精度,只使用粗略分类(fast/medium/slow)而非精确值;不将网络信息发送到服务器;仅在客户端本地使用这些信息来优化用户体验。
如何正确实现网络感知的资源加载?
应使用渐进增强策略:首先确保基础功能在所有网络条件下都能工作,然后根据 Network Information API 进行优化。根据 effectiveType 选择不同质量的资源(图片分辨率、视频码率等);根据 saveData 属性尊重用户的省流意愿;使用防抖避免频繁切换;在 API 不可用时提供合理的默认策略。
九、总结
Network Information API 为 Web 应用提供了获取设备网络连接信息的能力,主要特点包括:
-
网络类型检测:通过
effectiveType获取当前网络质量等级(4G/3G/2G/slow-2g)。 -
带宽和延迟估算:通过
downlink和rtt获取网络带宽和往返时间的估算值。 -
省流模式检测:通过
saveData检测用户是否启用了数据节省模式。 -
实时变化监听:通过
change事件实时响应网络状态变化。 -
在线/离线检测:结合
navigator.onLine和online/offline事件检测网络连接状态。
在使用 Network Information API 时,需要注意:
-
兼容性有限:仅 Chrome 和 Edge 支持,Firefox 和 Safari 不支持
-
估算值非精确值:downlink 和 rtt 是基于历史连接的估算
-
隐私风险:网络信息可能被用于指纹追踪
-
需要降级方案:API 不可用时
本文涉及AI创作
内容由AI创作,请仔细甄别