跳至主要內容
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 3GOffline 來模擬各種網路情境。

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


總結

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


延伸閱讀