Vue 3 与 Network Information API

1. 概述

Network Information API 是一个现代浏览器 API,用于获取设备的网络连接信息,如网络类型、带宽、延迟等。它允许 Web 应用根据设备的网络条件调整其行为,提供更好的用户体验和性能优化。

在 Vue 3 应用中,Network Information API 可以用于:

  • 根据网络类型调整资源加载策略
  • 切换不同质量的媒体内容
  • 优化数据同步和缓存策略
  • 提供网络状态反馈给用户
  • 实现智能的离线/在线切换
  • 优化电池使用

2. 核心知识

2.1 Network Information API 基础

Network Information API 提供了以下核心属性和事件:

属性/事件 描述
downlink 以 Mbps 为单位的有效带宽估计值
downlinkMax 以 Mbps 为单位的最大下行链路速度
effectiveType 网络类型的有效类型('slow-2g', '2g', '3g', '4g')
rtt 往返时间(Round-Trip Time),以毫秒为单位
saveData 指示用户是否启用了数据节省模式
type 网络连接类型('bluetooth', 'cellular', 'ethernet', 'none', 'wifi', 'wimax', 'other', 'unknown')
change 事件 当网络状态发生变化时触发

2.2 获取 Network Information 对象

// 从 navigator 对象获取网络信息
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

if (connection) {
  // 网络信息 API 可用
  console.log('网络类型:', connection.type);
  console.log('有效类型:', connection.effectiveType);
  console.log('下行带宽:', connection.downlink, 'Mbps');
  console.log('往返时间:', connection.rtt, 'ms');
  console.log('数据节省模式:', connection.saveData);
}

2.3 监听网络状态变化

const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

if (connection) {
  // 监听网络状态变化
  connection.addEventListener('change', () => {
    console.log('网络状态已变化:');
    console.log('新类型:', connection.type);
    console.log('新有效类型:', connection.effectiveType);
    console.log('新下行带宽:', connection.downlink, 'Mbps');
  });
}

3. Vue 3 与 Network Information API 集成

3.1 基础使用示例

<template>
  <div>
    <h2>Network Information API 示例</h2>
    <div class="network-status" :class="networkClass">
      <h3>当前网络状态</h3>
      <div class="status-item">
        <span class="label">网络类型:</span>
        <span class="value">{{ networkInfo.type }}</span>
      </div>
      <div class="status-item">
        <span class="label">有效类型:</span>
        <span class="value">{{ networkInfo.effectiveType }}</span>
      </div>
      <div class="status-item">
        <span class="label">下行带宽:</span>
        <span class="value">{{ networkInfo.downlink }} Mbps</span>
      </div>
      <div class="status-item">
        <span class="label">往返时间:</span>
        <span class="value">{{ networkInfo.rtt }} ms</span>
      </div>
      <div class="status-item">
        <span class="label">数据节省模式:</span>
        <span class="value">{{ networkInfo.saveData ? '开启' : '关闭' }}</span>
      </div>
      <div class="status-item">
        <span class="label">最大下行速度:</span>
        <span class="value">{{ networkInfo.downlinkMax || '未知' }} Mbps</span>
      </div>
    </div>
    <div class="content-section">
      <h3>根据网络状态调整内容</h3>
      <div class="content-example">
        <h4>媒体内容</h4>
        <div v-if="networkInfo.effectiveType === 'slow-2g' || networkInfo.effectiveType === '2g'">
          <p>📶 低质量视频(适合 2G 网络)</p>
          <video controls width="320" height="240">
            <source src="low-quality-video.mp4" type="video/mp4">
            您的浏览器不支持视频标签。
          </video>
        </div>
        <div v-else-if="networkInfo.effectiveType === '3g'">
          <p>📱 中等质量视频(适合 3G 网络)</p>
          <video controls width="640" height="480">
            <source src="medium-quality-video.mp4" type="video/mp4">
            您的浏览器不支持视频标签。
          </video>
        </div>
        <div v-else>
          <p>🚀 高质量视频(适合 4G/WiFi 网络)</p>
          <video controls width="1280" height="720">
            <source src="high-quality-video.mp4" type="video/mp4">
            您的浏览器不支持视频标签。
          </video>
        </div>
      </div>
      <div class="content-example">
        <h4>图片加载策略</h4>
        <div class="image-grid">
          <div v-for="i in 6" :key="i" class="image-item">
            <img 
              :src="getImageUrl(i)" 
              :alt="`示例图片 ${i}`" 
              :loading="networkInfo.effectiveType === '4g' ? 'eager' : 'lazy'"
            >
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';

const networkInfo = ref({
  type: 'unknown',
  effectiveType: 'unknown',
  downlink: 0,
  rtt: 0,
  saveData: false,
  downlinkMax: null
});

let connection = null;

// 更新网络信息
const updateNetworkInfo = () => {
  if (connection) {
    networkInfo.value = {
      type: connection.type,
      effectiveType: connection.effectiveType,
      downlink: connection.downlink,
      rtt: connection.rtt,
      saveData: connection.saveData,
      downlinkMax: connection.downlinkMax || null
    };
  }
};

// 网络状态变化的事件监听器
const handleNetworkChange = () => {
  updateNetworkInfo();
  console.log('网络状态已更新:', networkInfo.value);
};

// 计算网络状态的 CSS 类
const networkClass = computed(() => {
  const type = networkInfo.value.effectiveType;
  switch (type) {
    case 'slow-2g':
    case '2g':
      return 'network-slow';
    case '3g':
      return 'network-medium';
    case '4g':
      return 'network-fast';
    default:
      return 'network-unknown';
  }
});

// 根据网络类型获取图片 URL
const getImageUrl = (index) => {
  const quality = networkInfo.value.effectiveType === '4g' ? 'high' : 'low';
  return `https://picsum.photos/seed/${index}-${quality}/300/200`;
};

// 组件挂载时初始化
onMounted(() => {
  // 获取网络信息对象
  connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
  
  if (connection) {
    // 更新初始网络信息
    updateNetworkInfo();
    
    // 监听网络状态变化
    connection.addEventListener('change', handleNetworkChange);
  } else {
    console.log('Network Information API 不可用');
  }
});

// 组件卸载时清理
onUnmounted(() => {
  if (connection) {
    connection.removeEventListener('change', handleNetworkChange);
  }
});
</script>

<style scoped>
.network-status {
  margin: 20px 0;
  padding: 20px;
  border-radius: 8px;
  background-color: #f0f8ff;
  border: 1px solid #add8e6;
  transition: all 0.3s ease;
}

.network-slow {
  background-color: #fff0f0;
  border-color: #ffcccc;
}

.network-medium {
  background-color: #fff8e1;
  border-color: #ffe0b2;
}

.network-fast {
  background-color: #f0fff0;
  border-color: #ccffcc;
}

.network-unknown {
  background-color: #f5f5f5;
  border-color: #e0e0e0;
}

.status-item {
  display: flex;
  justify-content: space-between;
  margin: 10px 0;
  padding: 5px 0;
  border-bottom: 1px solid #eee;
}

.status-item:last-child {
  border-bottom: none;
}

.label {
  font-weight: bold;
  color: #666;
}

.value {
  font-weight: bold;
  color: #333;
}

.content-section {
  margin-top: 30px;
}

.content-example {
  margin: 20px 0;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
}

.image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  margin-top: 10px;
}

.image-item img {
  width: 100%;
  height: auto;
  border-radius: 4px;
  cursor: pointer;
  transition: transform 0.3s ease;
}

.image-item img:hover {
  transform: scale(1.05);
}

video {
  margin-top: 10px;
  border-radius: 4px;
  max-width: 100%;
}
</style>

3.2 创建可复用的 Network Information 组合式函数

// src/composables/useNetworkInformation.js
import { ref, onMounted, onUnmounted } from 'vue';

export function useNetworkInformation() {
  const networkInfo = ref({
    isSupported: false,
    type: 'unknown',
    effectiveType: 'unknown',
    downlink: 0,
    rtt: 0,
    saveData: false,
    downlinkMax: null
  });

  let connection = null;
  let changeHandler = null;

  // 更新网络信息
  const updateNetworkInfo = () => {
    if (connection) {
      networkInfo.value = {
        isSupported: true,
        type: connection.type,
        effectiveType: connection.effectiveType,
        downlink: connection.downlink,
        rtt: connection.rtt,
        saveData: connection.saveData,
        downlinkMax: connection.downlinkMax || null
      };
    }
  };

  // 初始化网络信息
  const initNetworkInfo = () => {
    connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
    
    if (connection) {
      networkInfo.value.isSupported = true;
      updateNetworkInfo();
      
      // 添加事件监听器
      changeHandler = () => updateNetworkInfo();
      connection.addEventListener('change', changeHandler);
    }
  };

  // 清理资源
  const cleanup = () => {
    if (connection && changeHandler) {
      connection.removeEventListener('change', changeHandler);
      connection = null;
      changeHandler = null;
    }
  };

  // 组件挂载时初始化
  onMounted(() => {
    initNetworkInfo();
  });

  // 组件卸载时清理
  onUnmounted(() => {
    cleanup();
  });

  return {
    networkInfo,
    updateNetworkInfo
  };
}

3.3 使用组合式函数的示例

<template>
  <div>
    <h2>使用 useNetworkInformation 组合式函数</h2>
    
    <!-- 网络状态显示 -->
    <div class="network-status-card" :class="networkStatusClass">
      <h3>当前网络状态</h3>
      <div v-if="networkInfo.isSupported">
        <div class="status-grid">
          <div class="status-item">
            <span class="status-label">类型:</span>
            <span class="status-value">{{ networkInfo.type }}</span>
          </div>
          <div class="status-item">
            <span class="status-label">有效类型:</span>
            <span class="status-value">{{ networkInfo.effectiveType }}</span>
          </div>
          <div class="status-item">
            <span class="status-label">下行带宽:</span>
            <span class="status-value">{{ networkInfo.downlink }} Mbps</span>
          </div>
          <div class="status-item">
            <span class="status-label">往返时间:</span>
            <span class="status-value">{{ networkInfo.rtt }} ms</span>
          </div>
          <div class="status-item">
            <span class="status-label">数据节省:</span>
            <span class="status-value">{{ networkInfo.saveData ? '开启' : '关闭' }}</span>
          </div>
        </div>
      </div>
      <div v-else class="not-supported">
        <p>Network Information API 不可用</p>
      </div>
    </div>
    
    <!-- 基于网络状态的内容 -->
    <div class="content-section">
      <h3>智能内容加载</h3>
      
      <!-- 资源加载策略 -->
      <div class="strategy-card">
        <h4>资源加载策略</h4>
        <p>当前策略: {{ resourceStrategy }}</p>
        <ul>
          <li v-for="(strategy, index) in availableStrategies" :key="index" class="strategy-item">
            <span :class="{ active: strategy.name === resourceStrategy }">
              {{ strategy.name }}: {{ strategy.description }}
            </span>
          </li>
        </ul>
      </div>
      
      <!-- 数据同步策略 -->
      <div class="strategy-card">
        <h4>数据同步策略</h4>
        <p>当前策略: {{ syncStrategy }}</p>
        <div class="sync-options">
          <button @click="syncData" :disabled="isSyncing" class="sync-btn">
            {{ isSyncing ? '同步中...' : '立即同步数据' }}
          </button>
          <span class="sync-status">{{ syncStatus }}</span>
        </div>
      </div>
      
      <!-- 媒体质量选择 -->
      <div class="strategy-card">
        <h4>媒体质量</h4>
        <div class="quality-options">
          <button 
            v-for="quality in mediaQualities" 
            :key="quality.value"
            @click="selectQuality(quality.value)"
            :class="{ active: selectedQuality === quality.value }"
          >
            {{ quality.label }}
          </button>
        </div>
        <p class="quality-hint">
          {{ qualityHint }}
        </p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { useNetworkInformation } from './composables/useNetworkInformation';

// 使用网络信息组合式函数
const { networkInfo } = useNetworkInformation();

const isSyncing = ref(false);
const syncStatus = ref('就绪');
const selectedQuality = ref('auto');

// 计算网络状态的 CSS 类
const networkStatusClass = computed(() => {
  if (!networkInfo.value.isSupported) return 'not-supported';
  
  const type = networkInfo.value.effectiveType;
  switch (type) {
    case 'slow-2g':
    case '2g':
      return 'network-slow';
    case '3g':
      return 'network-medium';
    case '4g':
      return 'network-fast';
    default:
      return 'network-unknown';
  }
});

// 可用的资源加载策略
const availableStrategies = [
  { name: 'aggressive', description: '立即加载所有资源,适合快速网络' },
  { name: 'balanced', description: '优先加载关键资源,延迟加载非关键资源' },
  { name: 'conservative', description: '仅加载当前需要的资源,适合慢速网络' }
];

// 计算资源加载策略
const resourceStrategy = computed(() => {
  if (!networkInfo.value.isSupported) return 'balanced';
  
  const type = networkInfo.value.effectiveType;
  if (type === '4g' && !networkInfo.value.saveData) {
    return 'aggressive';
  } else if (type === '3g' || networkInfo.value.saveData) {
    return 'balanced';
  } else {
    return 'conservative';
  }
});

// 计算数据同步策略
const syncStrategy = computed(() => {
  if (!networkInfo.value.isSupported) return 'manual';
  
  if (networkInfo.value.effectiveType === '4g' && !networkInfo.value.saveData) {
    return 'real-time';
  } else if (networkInfo.value.effectiveType === '3g') {
    return 'periodic';
  } else {
    return 'manual';
  }
});

// 媒体质量选项
const mediaQualities = [
  { value: 'auto', label: '自动' },
  { value: 'high', label: '高清' },
  { value: 'medium', label: '标清' },
  { value: 'low', label: '流畅' }
];

// 质量提示
const qualityHint = computed(() => {
  if (selectedQuality.value === 'auto') {
    const type = networkInfo.value.effectiveType;
    switch (type) {
      case '4g': return '根据网络自动选择:高清';
      case '3g': return '根据网络自动选择:标清';
      default: return '根据网络自动选择:流畅';
    }
  }
  return `手动选择:${mediaQualities.find(q => q.value === selectedQuality.value)?.label}`;
});

// 选择质量
const selectQuality = (quality) => {
  selectedQuality.value = quality;
};

// 同步数据
const syncData = async () => {
  isSyncing.value = true;
  syncStatus.value = '正在同步...';
  
  try {
    // 模拟数据同步
    await new Promise(resolve => {
      // 根据网络类型调整同步时间
      const delay = networkInfo.value.effectiveType === '4g' ? 1000 : 3000;
      setTimeout(resolve, delay);
    });
    
    syncStatus.value = '同步成功';
    setTimeout(() => {
      syncStatus.value = '就绪';
    }, 2000);
  } catch (error) {
    syncStatus.value = '同步失败';
  } finally {
    isSyncing.value = false;
  }
};
</script>

<style scoped>
.network-status-card {
  margin: 20px 0;
  padding: 20px;
  border-radius: 8px;
  background-color: #f5f5f5;
  border: 1px solid #e0e0e0;
  transition: all 0.3s ease;
}

.network-slow {
  background-color: #fff0f0;
  border-color: #ffcccc;
}

.network-medium {
  background-color: #fff8e1;
  border-color: #ffe0b2;
}

.network-fast {
  background-color: #f0fff0;
  border-color: #ccffcc;
}

.status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 15px;
  margin-top: 15px;
}

.status-item {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.status-label {
  font-size: 0.9em;
  color: #666;
}

.status-value {
  font-weight: bold;
  font-size: 1.1em;
  color: #333;
}

.not-supported {
  text-align: center;
  color: #666;
  padding: 20px;
}

.content-section {
  margin-top: 30px;
}

.strategy-card {
  margin: 20px 0;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
  background-color: #fafafa;
}

.strategy-item {
  margin: 10px 0;
  padding: 8px 12px;
  background-color: #f0f0f0;
  border-radius: 4px;
  transition: all 0.2s ease;
}

.strategy-item .active {
  background-color: #e3f2fd;
  border-left: 4px solid #2196f3;
  font-weight: bold;
}

.sync-options {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-top: 15px;
}

.sync-btn {
  padding: 8px 16px;
  background-color: #42b883;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.sync-btn:hover:not(:disabled) {
  background-color: #35495e;
}

.sync-btn:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.sync-status {
  font-weight: bold;
  color: #666;
}

.quality-options {
  display: flex;
  gap: 10px;
  margin: 15px 0;
}

.quality-options button {
  padding: 8px 16px;
  background-color: #f0f0f0;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.quality-options button:hover {
  background-color: #e0e0e0;
}

.quality-options button.active {
  background-color: #42b883;
  color: white;
  border-color: #42b883;
}

.quality-hint {
  color: #666;
  font-style: italic;
}
</style>

3.4 结合其他 API 的高级示例

<template>
  <div>
    <h2>Network Information API 高级应用</h2>
    
    <!-- 网络状态仪表板 -->
    <div class="dashboard">
      <div class="dashboard-card">
        <h3>网络概览</h3>
        <div class="network-metric">
          <span class="metric-label">连接类型</span>
          <span class="metric-value">{{ networkInfo.type }}</span>
        </div>
        <div class="network-metric">
          <span class="metric-label">有效类型</span>
          <span class="metric-value">{{ networkInfo.effectiveType }}</span>
        </div>
        <div class="network-metric">
          <span class="metric-label">数据节省</span>
          <span class="metric-value">{{ networkInfo.saveData ? '开启' : '关闭' }}</span>
        </div>
      </div>
      
      <div class="dashboard-card">
        <h3>性能指标</h3>
        <div class="metric-bar">
          <span class="metric-label">下行带宽</span>
          <div class="bar-container">
            <div class="bar" :style="{ width: bandwidthPercentage + '%' }"></div>
            <span class="bar-value">{{ networkInfo.downlink }} Mbps</span>
          </div>
        </div>
        <div class="metric-bar">
          <span class="metric-label">往返时间</span>
          <div class="bar-container">
            <div class="bar rtt" :style="{ width: rttPercentage + '%' }"></div>
            <span class="bar-value">{{ networkInfo.rtt }} ms</span>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 智能缓存策略 -->
    <div class="strategy-section">
      <h3>智能缓存策略</h3>
      <div class="cache-strategy" :class="cacheStrategyClass">
        <h4>{{ cacheStrategy }}</h4>
        <p>{{ cacheStrategyDescription }}</p>
        <button @click="clearCache" class="clear-cache-btn">清除缓存</button>
      </div>
    </div>
    
    <!-- 资源优化建议 -->
    <div class="suggestions-section">
      <h3>优化建议</h3>
      <ul class="suggestions-list">
        <li v-for="(suggestion, index) in suggestions" :key="index" class="suggestion-item">
          <span class="suggestion-icon">{{ suggestion.icon }}</span>
          <span class="suggestion-text">{{ suggestion.text }}</span>
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { useNetworkInformation } from './composables/useNetworkInformation';

// 使用网络信息组合式函数
const { networkInfo } = useNetworkInformation();

// 计算带宽百分比(0-100%)
const bandwidthPercentage = computed(() => {
  // 假设最大带宽为 100 Mbps
  return Math.min(Math.round((networkInfo.value.downlink / 100) * 100), 100);
});

// 计算 RTT 百分比(0-100%,值越低越好)
const rttPercentage = computed(() => {
  // 假设最大 RTT 为 1000 ms
  return Math.min(Math.round((1000 - networkInfo.value.rtt) / 1000 * 100), 100);
});

// 缓存策略
const cacheStrategy = computed(() => {
  if (!networkInfo.value.isSupported) return '标准缓存';
  
  if (networkInfo.value.effectiveType === '4g' && !networkInfo.value.saveData) {
    return '短期缓存';
  } else if (networkInfo.value.effectiveType === '3g') {
    return '平衡缓存';
  } else {
    return '长期缓存';
  }
});

// 缓存策略描述
const cacheStrategyDescription = computed(() => {
  switch (cacheStrategy.value) {
    case '短期缓存':
      return '网络条件良好,使用短期缓存以获取最新内容';
    case '平衡缓存':
      return '网络条件一般,使用平衡缓存策略';
    case '长期缓存':
      return '网络条件较差,使用长期缓存以减少数据使用';
    default:
      return '使用标准缓存策略';
  }
});

// 缓存策略 CSS 类
const cacheStrategyClass = computed(() => {
  switch (cacheStrategy.value) {
    case '短期缓存':
      return 'cache-short';
    case '平衡缓存':
      return 'cache-balanced';
    case '长期缓存':
      return 'cache-long';
    default:
      return 'cache-standard';
  }
});

// 生成优化建议
const suggestions = computed(() => {
  const list = [];
  
  if (networkInfo.value.effectiveType === 'slow-2g' || networkInfo.value.effectiveType === '2g') {
    list.push(
      { icon: '📱', text: '考虑使用低分辨率图像和视频' },
      { icon: '⏱️', text: '延迟加载非关键资源' },
      { icon: '💾', text: '增加缓存时间以减少网络请求' },
      { icon: '📊', text: '减少数据同步频率' }
    );
  } else if (networkInfo.value.effectiveType === '3g') {
    list.push(
      { icon: '⚖️', text: '优化图像大小和质量' },
      { icon: '🔄', text: '使用增量更新而非全量更新' },
      { icon: '📶', text: '考虑使用 CDN 加速' }
    );
  } else if (networkInfo.value.effectiveType === '4g') {
    list.push(
      { icon: '🚀', text: '可以使用更丰富的媒体内容' },
      { icon: '🔗', text: '考虑使用 WebSocket 进行实时通信' },
      { icon: '📈', text: '可以增加数据同步频率' }
    );
  }
  
  if (networkInfo.value.saveData) {
    list.push(
      { icon: '💡', text: '压缩资源以减少数据使用' },
      { icon: '🎨', text: '简化页面设计以减少资源加载' },
      { icon: '📣', text: '考虑禁用自动播放媒体' }
    );
  }
  
  return list;
});

// 清除缓存
const clearCache = async () => {
  try {
    await caches.keys().then(keys => {
      return Promise.all(
        keys.map(key => caches.delete(key))
      );
    });
    alert('缓存已清除');
  } catch (error) {
    alert('清除缓存失败');
  }
};
</script>

<style scoped>
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  margin: 20px 0;
}

.dashboard-card {
  padding: 20px;
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.network-metric {
  display: flex;
  justify-content: space-between;
  margin: 15px 0;
  padding: 10px 0;
  border-bottom: 1px solid #e9ecef;
}

.network-metric:last-child {
  border-bottom: none;
}

.metric-label {
  color: #6c757d;
  font-weight: 500;
}

.metric-value {
  font-weight: bold;
  color: #495057;
  font-size: 1.1em;
}

.metric-bar {
  margin: 15px 0;
}

.bar-container {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 5px;
}

.bar {
  flex: 1;
  height: 20px;
  background-color: #e3f2fd;
  border-radius: 10px;
  overflow: hidden;
  position: relative;
}

.bar::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: #2196f3;
  transition: width 0.3s ease;
}

.bar.rtt {
  background-color: #fff3e0;
}

.bar.rtt::after {
  background-color: #ff9800;
}

.bar-value {
  font-size: 0.9em;
  font-weight: bold;
  color: #495057;
  min-width: 80px;
  text-align: right;
}

.strategy-section {
  margin: 30px 0;
}

.cache-strategy {
  padding: 20px;
  border-radius: 8px;
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  transition: all 0.3s ease;
}

.cache-short {
  background-color: #e3f2fd;
  border-color: #bbdefb;
}

.cache-balanced {
  background-color: #fff3e0;
  border-color: #ffe0b2;
}

.cache-long {
  background-color: #f1f8e9;
  border-color: #dcedc8;
}

.cache-standard {
  background-color: #fafafa;
  border-color: #e0e0e0;
}

.clear-cache-btn {
  margin-top: 15px;
  padding: 8px 16px;
  background-color: #dc3545;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.clear-cache-btn:hover {
  background-color: #c82333;
}

.suggestions-section {
  margin: 30px 0;
}

.suggestions-list {
  list-style: none;
  padding: 0;
}

.suggestion-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 15px 0;
  padding: 15px;
  background-color: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 8px;
  transition: all 0.3s ease;
}

.suggestion-item:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.suggestion-icon {
  font-size: 1.5em;
  margin-top: 2px;
}

.suggestion-text {
  flex: 1;
  font-size: 1em;
  color: #495057;
}
</style>

4. 最佳实践

4.1 性能优化

  1. 仅在必要时使用 API

    • 不要在每个组件中都实例化网络信息监听
    • 考虑使用全局状态管理来共享网络状态
    • 避免在性能关键路径上频繁查询网络状态
  2. 合理处理不支持的情况

    • 为不支持 Network Information API 的浏览器提供回退策略
    • 不要假设 API 总是可用
    • 使用特性检测来检查 API 可用性
  3. 优化资源加载

    • 根据网络类型动态调整资源质量
    • 实现智能的预加载和延迟加载策略
    • 考虑使用自适应图片和视频
  4. 优化数据同步

    • 根据网络类型调整数据同步频率
    • 实现增量更新而非全量更新
    • 考虑使用后台同步 API

4.2 代码组织

  1. 使用组合式函数封装

    • 将网络信息逻辑封装到可复用的组合式函数中
    • 提供清晰的 API 接口
    • 自动处理组件生命周期
  2. 结合其他 API 使用

    • 与 Service Worker API 结合实现智能缓存
    • 与 Background Sync API 结合优化数据同步
    • 与 Cache API 结合优化资源加载
  3. 提供用户反馈

    • 向用户显示当前网络状态
    • 提供调整媒体质量的选项
    • 告知用户数据使用情况

4.3 浏览器兼容性

Network Information API 具有良好的浏览器支持,但存在一些差异:

  • Chrome 61+
  • Firefox 63+
  • Safari 14.1+
  • Edge 79+

对于不支持的浏览器,可以使用以下方式处理:

// 特性检测
if ('connection' in navigator) {
  // Network Information API 可用
} else {
  // 提供回退策略
}

5. 常见问题与解决方案

5.1 API 不可用

问题:Network Information API 在某些浏览器或环境中不可用

解决方案

  • 使用特性检测来检查 API 可用性
  • 为不支持的浏览器提供默认行为
  • 考虑使用第三方库来模拟 API 功能

5.2 网络状态检测不准确

问题:Network Information API 报告的网络状态不准确

解决方案

  • 理解 API 报告的是估计值,而非精确值
  • 结合其他指标(如实际下载速度测试)进行验证
  • 不要完全依赖单一指标,使用多个指标综合判断

5.3 频繁的网络状态变化

问题:网络状态频繁变化导致性能问题

解决方案

  • 使用防抖或节流来处理频繁的状态变化
  • 仅在关键决策时查询网络状态
  • 考虑使用缓存的网络状态,定期更新

5.4 不同浏览器的实现差异

问题:不同浏览器对 Network Information API 的实现存在差异

解决方案

  • 测试在不同浏览器中的行为
  • 处理不同浏览器的属性差异
  • 考虑使用 polyfill 来统一 API

6. 高级学习资源

6.1 官方文档

6.2 深入学习

6.3 相关工具和库

7. 实践练习

7.1 练习 1:实现自适应媒体播放器

目标:根据网络状态自动调整媒体质量

要求

  1. 创建一个媒体播放器组件
  2. 使用 Network Information API 获取网络状态
  3. 根据网络类型自动切换不同质量的媒体源
  4. 允许用户手动调整媒体质量
  5. 显示当前网络状态和媒体质量

7.2 练习 2:实现智能缓存策略

目标:根据网络状态调整缓存策略

要求

  1. 使用 Network Information API 获取网络状态
  2. 根据网络类型调整缓存时间
  3. 实现不同网络条件下的缓存策略
  4. 提供清除缓存的功能
  5. 显示当前缓存状态和策略

7.3 练习 3:实现网络状态监控仪表板

目标:创建一个实时监控网络状态的仪表板

要求

  1. 使用 Network Information API 获取网络状态
  2. 实时显示网络类型、有效类型、带宽、RTT 等指标
  3. 可视化显示网络性能指标
  4. 提供网络优化建议
  5. 实现响应式设计,适配不同设备

8. 总结

Network Information API 是一个强大的工具,用于获取和监控设备的网络连接信息。在 Vue 3 应用中,它可以帮助我们根据网络条件优化应用行为,提供更好的用户体验和性能。

通过创建可复用的组合式函数,我们可以将 Network Information API 的复杂性封装起来,提供简洁的 API 供组件使用。同时,我们需要注意性能优化、合理处理组件生命周期,以确保应用的高效运行。

在实际开发中,Network Information API 可以与其他现代浏览器 API(如 Service Worker、Cache API、Background Sync API 等)结合使用,实现更复杂的功能和更优的用户体验。

通过深入学习和实践 Network Information API,你将能够构建更智能、更高效的 Vue 3 应用,适应不同网络条件下的使用场景。

9. 代码示例下载

10. 后续学习建议

  1. 学习 Service Worker API 以实现更高级的缓存策略
  2. 深入研究 Background Sync API 以优化数据同步
  3. 学习 Cache API 以更精细地控制缓存
  4. 探索自适应设计模式
  5. 研究 Web Vitals 与网络性能的关系

通过综合运用这些技术,你将能够构建出在各种网络条件下都能提供出色用户体验的 Vue 3 应用。

« 上一篇 Vue 3 与 Performance Observer API 下一篇 » Vue 3 与 Battery Status API