如何用 JavaScript 判断用户网络状况?教你几招实用方法

摘要:做网页开发的时候,我们经常需要知道用户现在的网络好不好。比如看视频的时候,网好就放高清,网差就放流畅版,断网了就提醒用户。今天我就来聊聊怎么用 JavaScript 判断用户网络情况,包括是在线还是离线,网速快不快。

做网页开发的时候,我们经常需要知道用户现在的网络好不好。比如看视频的时候,网好就放高清,网差就放流畅版,断网了就提醒用户。今天我就来聊聊怎么用 JavaScript 判断用户网络情况,包括是在线还是离线,网速快不快。


一、怎么判断用户在线还是离线?

浏览器自带了一个简单的判断方法:navigator.onLine。这个属性会返回 true 或 false,告诉你当前浏览器认为网络通不通。

// 看看当前网络状态
if (navigator.onLine) {
  console.log('网络在线');
} else {
  console.log('网络已断开');
}

// 监听网络变化
window.addEventListener('online', () => {
  console.log('网络连上了');
});

window.addEventListener('offline', () => {
  console.log('网络断了');
});

但这个方法有个问题:它只能判断浏览器有没有连接到网络,判断不了这个网络能不能真的访问你的服务器。举个例子,你连着商场的WiFi,但没登录认证,浏览器显示在线,实际上没法上网。这时候就需要更靠谱的方法。


二、用心跳检测判断网络是否真的可用

心跳检测就像是你每隔一段时间给你的服务器发个消息:“在吗?”如果服务器回复了,说明网络是好的。要是连着几次都没回复,那就说明网络可能真的出问题了。

下面是一个简单实用的心跳检测代码:

class NetworkCheck {
  constructor(options = {}) {
    this.options = {
      checkUrl: '/api/ping',      // 检测的接口地址
      interval: 30000,             // 每30秒检测一次
      timeout: 5000,               // 5秒超时
      onStatusChange: null,        // 状态改变时的回调
      ...options
    };
    
    this.isOnline = navigator.onLine;
    this.checking = false;
    this.timer = null;
    this.failCount = 0;
    this.maxFailCount = 2;          // 连续失败2次就判定为断网
    
    this.init();
  }

  init() {
    // 监听浏览器的网络事件
    window.addEventListener('online', () => this.onBrowserOnline());
    window.addEventListener('offline', () => this.onBrowserOffline());
    
    this.startCheck();
  }

  startCheck() {
    this.stopCheck();
    this.timer = setInterval(() => this.check(), this.options.interval);
    this.check();  // 立即检测一次
  }

  stopCheck() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  async check() {
    if (this.checking) return;
    
    this.checking = true;
    
    try {
      // 发起一个轻量级请求
      const response = await fetch(this.options.checkUrl, {
        method: 'HEAD',
        cache: 'no-cache',
        headers: {
          'X-Network-Check': 'ping'
        }
      });
      
      // 请求成功,网络正常
      this.onSuccess();
      
    } catch (error) {
      // 请求失败
      this.onFail();
    } finally {
      this.checking = false;
    }
  }

  onSuccess() {
    this.failCount = 0;
    
    if (!this.isOnline) {
      this.setStatus(true);
    }
  }

  onFail() {
    this.failCount++;
    
    // 连续失败达到设定次数,判定为断网
    if (this.failCount >= this.maxFailCount && this.isOnline) {
      this.setStatus(false);
    }
  }

  onBrowserOnline() {
    // 浏览器说在线了,再验证一下
    this.check();
  }

  onBrowserOffline() {
    this.setStatus(false);
  }

  setStatus(status) {
    if (this.isOnline !== status) {
      this.isOnline = status;
      
      if (this.options.onStatusChange) {
        this.options.onStatusChange(status);
      }
      
      // 也可以发个自定义事件,方便其他地方监听
      window.dispatchEvent(new CustomEvent('netstatus', {
        detail: { online: status }
      }));
    }
  }

  async checkNow() {
    return this.check();
  }

  destroy() {
    this.stopCheck();
    window.removeEventListener('online', this.onBrowserOnline);
    window.removeEventListener('offline', this.onBrowserOffline);
  }
}

// 使用方法
const checker = new NetworkCheck({
  checkUrl: 'https://你的域名/api/ping',
  interval: 15000,
  onStatusChange: (online) => {
    if (online) {
      console.log('网络恢复了,可以同步数据');
      // 这里可以触发数据同步等操作
    } else {
      console.log('网络断了,进入离线模式');
      // 这里可以保存数据到本地
    }
  }
});

后端只需要提供一个简单的接口,返回200状态码就行:

// Node.js + Express 示例
app.head('/api/ping', (req, res) => {
  res.status(200).end();
});


三、怎么测网速?

知道了网络通不通还不够,有时候我们还想知道网速快不快,这样才能决定给用户展示什么内容。

1. 浏览器自带的网速信息(不推荐)

浏览器有个 navigator.connection 接口,可以拿到一些网络信息:

console.log('网络类型:', navigator.connection.effectiveType); // 4g, 3g, 2g
console.log('下行速度:', navigator.connection.downlink, 'Mbps');

但这个接口有几个问题:一是兼容性不好,很多浏览器不支持;二是数据不太准,跟实际测速结果有差距。所以实际项目中很少用它。

2. 图片测速法(推荐)

这个方法很简单:让浏览器下载一张图片,记录下载时间,用图片大小除以时间就能算出大概的网速。

class SpeedTest {
  constructor() {
    this.speed = 0;
  }

  // 测速
  async test() {
    // 准备一张图片,大小大概100KB左右
    const imgUrl = 'https://你的域名/speed-test.jpg?t=' + Date.now();
    const startTime = Date.now();
    
    try {
      const response = await fetch(imgUrl);
      const blob = await response.blob();
      const endTime = Date.now();
      
      // 图片大小(字节)
      const size = blob.size;
      // 下载时间(秒)
      const duration = (endTime - startTime) / 1000;
      // 计算速度(KB/秒)
      const speedKBps = size / duration / 1024;
      // 换算成Mbps(1Mbps = 128KB/秒)
      const speedMbps = speedKBps / 128;
      
      this.speed = speedMbps;
      
      return {
        speedMbps: speedMbps.toFixed(2),
        level: this.getLevel(speedMbps)
      };
    } catch (error) {
      console.log('测速失败', error);
      return null;
    }
  }
  
  // 根据网速判断网络等级
  getLevel(speedMbps) {
    if (speedMbps < 0.5) return '慢速';
    if (speedMbps < 2) return '中速';
    return '高速';
  }
}

// 使用
const tester = new SpeedTest();
const result = await tester.test();
if (result) {
  console.log(`网速:${result.speedMbps}Mbps,属于${result.level}网络`);
}

为了提高准确率,可以测两次取平均值:

async function getAverageSpeed() {
  const tester = new SpeedTest();
  const speeds = [];
  
  // 测两次
  for (let i = 0; i < 2; i++) {
    const result = await tester.test();
    if (result) {
      speeds.push(parseFloat(result.speedMbps));
    }
    // 间隔一下再测第二次
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  
  if (speeds.length === 0) return null;
  
  const avg = speeds.reduce((a, b) => a + b, 0) / speeds.length;
  return {
    speedMbps: avg.toFixed(2),
    level: avg < 0.5 ? '慢速' : (avg < 2 ? '中速' : '高速')
  };
}


总结

  1. 判断网络通不通,先用浏览器的 navigator.onLine 快速判断,再用心跳检测确认后端接口能不能访问。

  2. 心跳检测要设置超时时间和失败次数,避免误判。

  3. 测网速推荐用图片测速法,简单可靠,兼容性好。

  4. 实际项目中,可以结合这两种方法:先判断网络是否通畅,再根据网速决定给用户展示什么内容。

这样就能根据用户的不同网络情况,提供更好的使用体验了。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_13485