最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript檢測(cè)網(wǎng)絡(luò)連接狀態(tài)以及網(wǎng)絡(luò)測(cè)速的具體方案

 更新時(shí)間:2026年03月19日 08:46:02   作者:程序員小寒  
在實(shí)際開(kāi)發(fā)項(xiàng)目中,有時(shí)候需要拿到用戶(hù)網(wǎng)絡(luò)狀態(tài),然后根據(jù)用戶(hù)的網(wǎng)絡(luò)情況進(jìn)行優(yōu)化,但在實(shí)際開(kāi)發(fā)中,我們一般會(huì)劃分成斷網(wǎng)、弱網(wǎng)、強(qiáng)網(wǎng)三個(gè)等級(jí),并針對(duì)等級(jí)進(jìn)行優(yōu)化,那么 JavaScript 中如何檢測(cè)用戶(hù)網(wǎng)絡(luò)狀態(tài)呢?下面小編為大家詳細(xì)說(shuō)說(shuō)

引言

在實(shí)際開(kāi)發(fā)項(xiàng)目中,有時(shí)候需要拿到用戶(hù)網(wǎng)絡(luò)狀態(tài),然后根據(jù)用戶(hù)的網(wǎng)絡(luò)情況進(jìn)行優(yōu)化。常見(jiàn)的網(wǎng)絡(luò)狀態(tài)有離線(xiàn) offline、WiFi、2G/3G、4G、5G 等,但在實(shí)際開(kāi)發(fā)中,我們一般會(huì)劃分成斷網(wǎng)、弱網(wǎng)、強(qiáng)網(wǎng)三個(gè)等級(jí),并針對(duì)等級(jí)進(jìn)行優(yōu)化。比如在視頻播放時(shí),可以根據(jù)不同網(wǎng)絡(luò)情況給用戶(hù)分別默認(rèn)播放不同清晰度的視頻(高清、標(biāo)清、流暢等)。

那么 JavaScript 中如何檢測(cè)用戶(hù)網(wǎng)絡(luò)狀態(tài)呢?

1、網(wǎng)絡(luò)狀態(tài):在線(xiàn) online Or 離線(xiàn) offline?

HTML5 規(guī)范提供了 navigator.onLine 來(lái)拿到瀏覽器的在線(xiàn)狀態(tài)。該屬性返回一個(gè)布爾值,true 表示在線(xiàn),false 表示離線(xiàn)。同時(shí)提供了 onlineoffline 事件,通過(guò)監(jiān)聽(tīng)這兩個(gè)事件,可以動(dòng)態(tài)監(jiān)聽(tīng)到網(wǎng)絡(luò)連接狀態(tài)變化。

// 監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)
window.addEventListener('online', () => {
  console.log('網(wǎng)絡(luò)已連接');
  // 觸發(fā)數(shù)據(jù)同步...
});

window.addEventListener('offline', () => {
  console.log('網(wǎng)絡(luò)已斷開(kāi)');
  // 進(jìn)入離線(xiàn)模式...
});

// 實(shí)時(shí)獲取當(dāng)前狀態(tài)
console.log('當(dāng)前網(wǎng)絡(luò)狀態(tài):', navigator.onLine ? '在線(xiàn)' : '離線(xiàn)');

但是 navigator.onLine 有一定的局限性,比如在某些情況下,網(wǎng)絡(luò)是連接的,它也返回 true 了,但實(shí)際上無(wú)法訪(fǎng)問(wèn)你的后端 API:

  • WiFi連接了但無(wú)互聯(lián)網(wǎng)訪(fǎng)問(wèn)(比如公共圖書(shū)館需要登錄認(rèn)證的公共 WiFi)。
  • 網(wǎng)絡(luò)延遲極高(理論上在線(xiàn),但請(qǐng)求超時(shí))。
  • DNS解析失敗。

我們需要更加準(zhǔn)確的判斷用戶(hù)此刻是否網(wǎng)絡(luò)能正常訪(fǎng)問(wèn)服務(wù)端,這就需要做心跳檢測(cè)

2、心跳檢測(cè)

顧名思義,心跳檢測(cè)就像人的心跳一樣,每隔一段時(shí)間跳動(dòng)一下,告訴外界"我還活著"。它是一種用于判斷連接對(duì)端(如服務(wù)器或客戶(hù)端)是否存活的機(jī)制。它常常用于 WebSocket 連接,用來(lái)解決非正常關(guān)閉造成的"死連接"問(wèn)題。我們這里可以借用它檢測(cè)客戶(hù)端“是否存活”的能力,來(lái)判斷客戶(hù)端的網(wǎng)絡(luò)是否正常訪(fǎng)問(wèn)后端 API。

心跳檢測(cè)客戶(hù)端代碼:

// client.js
class NetworkMonitor {
  constructor(options = {}) {
    this.options = {
      pingUrl: '/api/ping',        // 心跳檢測(cè)接口
      interval: 30000,              // 30秒檢測(cè)一次
      timeout: 5000,                 // 5秒超時(shí)
      onStatusChange: null,          // 狀態(tài)變化回調(diào)
      ...options
    };
    
    this.isOnline = navigator.onLine;  // 當(dāng)前網(wǎng)絡(luò)狀態(tài)
    this.checking = false;              // 是否正在檢測(cè)
    this.timer = null;                  // 定時(shí)器
    this.consecutiveFailures = 0;       // 連續(xù)失敗次數(shù)
    this.maxFailures = 2;                // 最大允許失敗次數(shù)
    
    this.init();
  }
  
  init() {
    // 監(jiān)聽(tīng)瀏覽器原生事件
    window.addEventListener('online', () => this.handleBrowserOnline());
    window.addEventListener('offline', () => this.handleBrowserOffline());
    
    // 啟動(dòng)心跳檢測(cè)
    this.startHeartbeat();
  }
  
  startHeartbeat() {
    this.stopHeartbeat();
    this.timer = setInterval(() => this.checkNetwork(), this.options.interval);
    // 立即執(zhí)行一次
    this.checkNetwork();
  }
  
  stopHeartbeat() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }
  
  async checkNetwork() {
    if (this.checking) return;
    
    this.checking = true;
    const startTime = Date.now();
    
    try {
      // 使用fetch發(fā)起一個(gè)輕量級(jí)請(qǐng)求
      const response = await fetch(this.options.pingUrl, {
        method: 'HEAD',  // 只獲取頭部,減少數(shù)據(jù)傳輸
        mode: 'no-cors', // 如果跨域且無(wú)需讀取響應(yīng)
        cache: 'no-cache',
        headers: {
          'X-Network-Check': 'ping'
        }
      });
      
      // 請(qǐng)求成功
      this.handleSuccess();
      
    } catch (error) {
      // 請(qǐng)求失?。ǔ瑫r(shí)、網(wǎng)絡(luò)錯(cuò)誤等)
      this.handleFailure();
    } finally {
      this.checking = false;
    }
  }
  
  handleSuccess() {
    // 重置失敗計(jì)數(shù)
    this.consecutiveFailures = 0;
    
    // 如果之前是離線(xiàn),現(xiàn)在變?yōu)樵诰€(xiàn)
    if (!this.isOnline) {
      this.setOnlineStatus(true);
    }
  }
  
  handleFailure() {
    this.consecutiveFailures++;
    
    // 連續(xù)失敗達(dá)到閾值,判定為離線(xiàn)
    if (this.consecutiveFailures >= this.maxFailures && this.isOnline) {
      this.setOnlineStatus(false);
    }
  }
  
  handleBrowserOnline() {
    // 瀏覽器說(shuō)在線(xiàn)了,但我們?nèi)孕栩?yàn)證真正的API可達(dá)性
    console.log('瀏覽器報(bào)告網(wǎng)絡(luò)已連接,開(kāi)始驗(yàn)證...');
    this.checkNetwork();
  }
  
  handleBrowserOffline() {
    // 瀏覽器明確說(shuō)離線(xiàn),直接更新?tīng)顟B(tài)
    this.setOnlineStatus(false);
  }
  
  setOnlineStatus(status) {
    if (this.isOnline !== status) {
      this.isOnline = status;
      
      console.log(`網(wǎng)絡(luò)狀態(tài)變更為: ${status ? '在線(xiàn)' : '離線(xiàn)'}`);
      
      // 觸發(fā)回調(diào)
      if (this.options.onStatusChange) {
        this.options.onStatusChange(status);
      }
      
      // 觸發(fā)自定義事件,方便其他地方監(jiān)聽(tīng)
      window.dispatchEvent(new CustomEvent('network-status-change', {
        detail: { isOnline: status }
      }));
    }
  }
  
  // 手動(dòng)觸發(fā)一次檢測(cè)
  async checkNow() {
    return this.checkNetwork();
  }
  
  // 銷(xiāo)毀
  destroy() {
    this.stopHeartbeat();
    window.removeEventListener('online', this.handleBrowserOnline);
    window.removeEventListener('offline', this.handleBrowserOffline);
  }
}

// 使用示例
const monitor = new NetworkMonitor({
  pingUrl: 'https://your-api.com/api/ping',
  interval: 15000,  // 15秒檢測(cè)一次
  timeout: 3000,
  onStatusChange: (isOnline) => {
    if (isOnline) {
      // 網(wǎng)絡(luò)恢復(fù),觸發(fā)數(shù)據(jù)同步
      syncWithServer();
      showToast('網(wǎng)絡(luò)已恢復(fù),正在同步數(shù)據(jù)...');
    } else {
      // 網(wǎng)絡(luò)斷開(kāi),進(jìn)入離線(xiàn)模式
      enableOfflineMode();
      showToast('網(wǎng)絡(luò)已斷開(kāi),進(jìn)入離線(xiàn)模式');
    }
  }
});

心跳檢測(cè)服務(wù)端代碼:

// server.js
const express = require('express');
const app = express();
const port = 3000;

// 心跳檢測(cè)接口
app.head('/api/ping', (req, res) => {
  // 可選:記錄心跳日志
  console.log(`[${new Date().toISOString()}] 收到心跳檢測(cè)請(qǐng)求 from ${req.ip}`);
  
  // 可選:檢查自定義頭
  if (req.headers['x-network-check'] === 'ping') {
    console.log('網(wǎng)絡(luò)健康檢查請(qǐng)求');
  }
  
  // 只需返回 200 狀態(tài)碼即可
  res.status(200).end();
});

// 如果需要 GET 也支持(某些場(chǎng)景可能需要)
app.get('/api/ping', (req, res) => {
  // 可以返回一些輕量級(jí)數(shù)據(jù)
  res.json({ 
    status: 'ok', 
    timestamp: Date.now(),
    message: 'success' 
  });
});

// 啟動(dòng)服務(wù)器
app.listen(port, () => {
  console.log(`網(wǎng)絡(luò)監(jiān)控服務(wù)運(yùn)行在 http://localhost:${port}`);
});

3、網(wǎng)速檢測(cè)

3.1 navigator.connection

HTML5 規(guī)范提供了 navigator.connection 可以獲取網(wǎng)絡(luò)類(lèi)型 effectiveType 和網(wǎng)絡(luò)下行速度 downlink。

console.log("網(wǎng)絡(luò)狀態(tài):" + navigator.connection.effectiveType); // 比如:4g/3g/2g
console.log("網(wǎng)絡(luò)下行速度:" + navigator.connection.downlink  + "MB/S"); // 比如:10MB/S

navigator.connection 存在兩個(gè)問(wèn)題,第一個(gè)是兼容性很差,像 Edge、Firefox、Safari 這種主流瀏覽器都不支持。

第二個(gè)是數(shù)據(jù)不準(zhǔn)確,與一些測(cè)速器的結(jié)果有一定的偏差?;谶@兩個(gè)問(wèn)題,實(shí)際項(xiàng)目中不會(huì)采用 navigator.connection 方案。

3.2 圖片測(cè)速法

圖片測(cè)速法就是通過(guò)客戶(hù)端加載圖片,拿到圖片大小和加載時(shí)間,進(jìn)而算出用戶(hù)的網(wǎng)速,其具體步驟如下:

  1. 加載兩張圖片,一張極小的(比如 1x1像素),一張稍大的(比如 2x2像素)。
  2. 記錄圖片請(qǐng)求開(kāi)始時(shí)間,以及圖片 onLoad 完成的時(shí)間。
  3. 計(jì)算速度:圖片大小 / 加載時(shí)間 = 網(wǎng)速
  4. 取平均值,把兩個(gè)數(shù)據(jù)取個(gè)平均值,目的是減少誤差。

小結(jié)

  1. 瀏覽器可以通過(guò) navigator.onLine 接口拿到瀏覽器的在線(xiàn)狀態(tài),并通過(guò)監(jiān)聽(tīng) onlineoffline 事件監(jiān)聽(tīng)網(wǎng)絡(luò)變化。
  2. 由于 navigator.onLine 存在一定的局限性,比如無(wú)法識(shí)別到 WiFi 連接了但無(wú)互聯(lián)網(wǎng)訪(fǎng)問(wèn)、網(wǎng)絡(luò)延遲極高、DNS 解析失敗等情況,我們還需要通過(guò)心跳檢測(cè)方案來(lái)判斷客戶(hù)端是否能正常訪(fǎng)問(wèn)后端 API。
  3. navigator.connection 雖然能拿到用戶(hù)的網(wǎng)絡(luò)下行速度 downlink,但 connection 接口存在兼容性和數(shù)據(jù)不準(zhǔn)的問(wèn)題,所以我們需要用圖片測(cè)速法來(lái)更加準(zhǔn)確地拿到用戶(hù)網(wǎng)速情況。

以上就是JavaScript檢測(cè)網(wǎng)絡(luò)連接狀態(tài)以及網(wǎng)絡(luò)測(cè)速的具體方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript檢測(cè)網(wǎng)絡(luò)連接狀態(tài)及測(cè)速的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS實(shí)現(xiàn)非首屏圖片延遲加載的示例

    JS實(shí)現(xiàn)非首屏圖片延遲加載的示例

    下面小編就為大家分享一篇用JS實(shí)現(xiàn)非首屏圖片延遲加載的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • javascript實(shí)例--教你實(shí)現(xiàn)撲克牌洗牌功能

    javascript實(shí)例--教你實(shí)現(xiàn)撲克牌洗牌功能

    我們一般都會(huì)按照順序把隨機(jī)摸過(guò)來(lái)的牌從小到大的順序在手上理整齊(記得小時(shí)候打牌兩副牌手都抓不過(guò)來(lái)),這篇隨筆就是想通過(guò)實(shí)現(xiàn)這個(gè)功能來(lái)熟悉下js中排序數(shù)組等相關(guān)知識(shí)。
    2014-05-05
  • JavaScript獲取短信驗(yàn)證碼(周期性)

    JavaScript獲取短信驗(yàn)證碼(周期性)

    這篇文章主要為大家詳細(xì)介紹了JavaScript周期性獲取短信驗(yàn)證碼的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • JS forEach和map方法的用法與區(qū)別分析

    JS forEach和map方法的用法與區(qū)別分析

    這篇文章主要介紹了JS forEach和map方法的用法與區(qū)別,結(jié)合實(shí)例形式分析了forEach和map方法的功能、原理、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-02-02
  • 詳解webpack引用jquery(第三方模塊)的三種辦法

    詳解webpack引用jquery(第三方模塊)的三種辦法

    這篇文章主要介紹了詳解webpack引用jquery(第三方模塊)的三種辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 微信小程序 接入騰訊地圖的兩種寫(xiě)法

    微信小程序 接入騰訊地圖的兩種寫(xiě)法

    這篇文章主要介紹了微信小程序 接入騰訊地圖的兩種寫(xiě)法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 同步異步動(dòng)態(tài)引入js文件的幾種方法總結(jié)

    同步異步動(dòng)態(tài)引入js文件的幾種方法總結(jié)

    下面小編就為大家?guī)?lái)一篇同步異步動(dòng)態(tài)引入js文件的幾種方法總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-09-09
  • js尾調(diào)用優(yōu)化的實(shí)現(xiàn)

    js尾調(diào)用優(yōu)化的實(shí)現(xiàn)

    這篇文章主要介紹了js尾調(diào)用優(yōu)化的實(shí)現(xiàn),尾調(diào)用(Tail Call)是函數(shù)式編程的一個(gè)重要概念,本文介紹它的含義和用法。感興趣的可以了解一下
    2019-05-05
  • js實(shí)現(xiàn)圖片放大展示效果

    js實(shí)現(xiàn)圖片放大展示效果

    這篇文章主要介紹了js實(shí)現(xiàn)圖片放大展示效果,點(diǎn)擊圖片可查看放大效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • JavaScript中eval()函數(shù)用法詳解

    JavaScript中eval()函數(shù)用法詳解

    eval() 函數(shù)計(jì)算 JavaScript 字符串,并把它作為腳本代碼來(lái)執(zhí)行,本文給大家
    2015-12-12

最新評(píng)論

龙海市| 华容县| 霍州市| 周至县| 利辛县| 胶南市| 呈贡县| 侯马市| 炎陵县| 库尔勒市| 遵化市| 武强县| 昌黎县| 皮山县| 托克逊县| 南宫市| 四川省| 克山县| 庆阳市| 保靖县| 栾川县| 九龙坡区| 黔江区| 陇南市| 昌江| 遂川县| 比如县| 无为县| 濮阳县| 安达市| 沅陵县| 肥乡县| 大石桥市| 大化| 赤城县| 琼结县| 根河市| 峨山| 宁晋县| 金堂县| 永州市|