跳至主要內容
Skip to content

偵測網路狀態:Network Information API 與 onLine ​

在行動優先的時代,網路連線的不穩定是常態。為了提供更好的使用者體驗(例如:在斷線時暫存資料、在弱網環境下載低解析度圖片),我們需要能夠即時偵測使用者的網路狀態。

本篇將整合標準的 navigator.onLine 與進階的 Network Information API,打造一個全方位的網路狀態監測 Composable。


一、 核心 API 介紹 ​

1.1 navigator.onLine ​

這是所有瀏覽器都支援的基礎 API,只提供一個布林值:

  • true: 瀏覽器已連接到網路(或區域網路)。
  • false: 瀏覽器已離線。

WARNING

限制:true 只代表電腦有連接到路由器或區域網路,不代表一定能連上網際網路。

1.2 Network Information API ​

這是一個實驗性 API(目前主要支援 Chromium 核心),提供更詳細的連線資訊,掛載於 navigator.connection:

  • effectiveType: 有效連線類型 ('4g', '3g', '2g', 'slow-2g')。
  • downlink: 估計下行速度 (Mbps)。
  • rtt: 往返延遲時間 (ms)。
  • saveData: 使用者是否開啟「節省流量」模式。

二、 封裝 useNetwork Composable ​

我們將這兩個 API 結合,提供一個完整的網路狀態監測工具。

2.1 介面定義 ​

typescript
export type NetworkType = 'bluetooth' | 'cellular' | 'ethernet' | 'none' | 'wifi' | 'wimax' | 'other' | 'unknown';
export type NetworkEffectiveType = 'slow-2g' | '2g' | '3g' | '4g';

export interface NetworkState {
  isOnline: boolean;
  offlineAt: number | null;
  downlink?: number;
  downlinkMax?: number;
  effectiveType?: NetworkEffectiveType;
  rtt?: number;
  saveData?: boolean;
  type?: NetworkType;
}

2.2 完整實作 ​

typescript
// composables/useNetwork.ts
import { ref, reactive, onMounted, onUnmounted } from 'vue';

export function useNetwork() {
  const isSupported = typeof navigator !== 'undefined' && 'connection' in navigator;
  
  const state = reactive<NetworkState>({
    isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,
    offlineAt: null,
    // 以下屬性僅 Chromium 支援,預設為 undefined
    downlink: undefined,
    effectiveType: undefined,
    rtt: undefined,
    saveData: undefined,
    type: undefined
  });

  const updateNetworkInformation = () => {
    if (!isSupported) return;
    
    const connection = (navigator as any).connection;
    state.downlink = connection.downlink;
    state.effectiveType = connection.effectiveType;
    state.rtt = connection.rtt;
    state.saveData = connection.saveData;
    state.type = connection.type;
  };

  const onOnline = () => {
    state.isOnline = true;
    state.offlineAt = null;
  };

  const onOffline = () => {
    state.isOnline = false;
    state.offlineAt = Date.now();
  };

  const onConnectionChange = () => {
    updateNetworkInformation();
  };

  onMounted(() => {
    if (typeof window === 'undefined') return;

    window.addEventListener('online', onOnline);
    window.addEventListener('offline', onOffline);

    if (isSupported) {
      const connection = (navigator as any).connection;
      connection.addEventListener('change', onConnectionChange);
      updateNetworkInformation(); // 初始化數值
    }
  });

  onUnmounted(() => {
    if (typeof window === 'undefined') return;

    window.removeEventListener('online', onOnline);
    window.removeEventListener('offline', onOffline);

    if (isSupported) {
      const connection = (navigator as any).connection;
      connection.removeEventListener('change', onConnectionChange);
    }
  });

  return {
    ...state, // 展開屬性以便直接解構使用
    state     // 也保留 reactive 物件本身
  };
}

三、 實戰範例:智慧型圖片載入與斷線提示 ​

這個範例展示如何根據網路狀況做兩件事:

  1. 斷線通知:當偵測到斷線時,顯示全域警告。
  2. 適應性載入:如果是弱網 (2g/slow-2g) 或開啟省流模式,載入低解析度圖片。
vue
<script setup lang="ts">
import { computed } from 'vue';
import { useNetwork } from '@/composables/useNetwork';

const { isOnline, effectiveType, saveData } = useNetwork();

// 判斷是否為弱網環境
const isSlowConnection = computed(() => {
  return saveData || effectiveType === '2g' || effectiveType === 'slow-2g';
});

// 根據網速決定圖片來源
const imageUrl = computed(() => {
  return isSlowConnection.value
    ? 'https://example.com/image-low-res.jpg'  // 弱網:載入小圖
    : 'https://example.com/image-high-res.jpg'; // 強網:載入大圖
});
</script>

<template>
  <div class="network-demo">
    
    <!-- 斷線警告 -->
    <div v-if="!isOnline" class="offline-banner">
      網路連接已中斷,目前顯示的是快取內容。
    </div>

    <div class="content">
      <h3>網路狀態面板</h3>
      <ul>
        <li>連線狀態: <span :class="{ online: isOnline }">{{ isOnline ? '連線中' : '已離線' }}</span></li>
        <li>連線類型: {{ effectiveType || '未知 (不支援)' }}</li>
        <li>節省流量模式: {{ saveData ? '開啟' : '關閉' }}</li>
      </ul>

      <div class="image-wrapper">
        <p>目前載入的是:{{ isSlowConnection ? '低畫質 (省流)' : '高畫質 (原始)' }} 版圖片</p>
        <img :src="imageUrl" alt="Demo Image" />
      </div>
    </div>
  </div>
</template>

<style scoped>
.offline-banner {
  background: #ff4757;
  color: white;
  text-align: center;
  padding: 10px;
  position: sticky;
  top: 0;
}
.online {
  color: #2ed573;
  font-weight: bold;
}
.network-demo {
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
}
.content {
  padding: 20px;
}
.image-wrapper img {
  max-width: 100%;
  border-radius: 4px;
  margin-top: 10px;
}
</style>

四、 開發者工具模擬測試 ​

要測試這個功能,不需要真的拔網路線或跑到地下室。Chrome DevTools 提供了強大的模擬功能:

  1. 打開 DevTools (F12)。
  2. 切換到 Network 頁籤。
  3. 在上方工具列找到 No throttling 下拉選單。
  4. 選擇 Fast 3G, Slow 3G 或 Offline 來模擬各種網路情境。

當你切換時,navigator.connection 的 change 事件就會被觸發,你的 Vue 元件也應該即時反應。


總結 ​

透過整合 onLine 事件與 Network Information API,我們可以讓網頁應用程式具備「網路感知 (Network Aware)」的能力。這對於打造漸進式網頁應用 (PWA) 或優化使用者在不同網路環境下的體驗至關重要。


延伸閱讀 ​