浏览器中如何获取用户网络状态

3 min read

网页开发中存在需要获取用户是否在线的场景及用户网络状态,浏览器提供了 navigator.onLinenavigator.connection 可以实现这一需求。

获取在线状态

if (navigator.onLine) {
  console.log("online");
} else {
  console.log("offline");
}

监听网络状态变更事件

window.addEventListener("offline", (e) => {
  console.log("offline");
});
 
window.addEventListener("online", (e) => {
  console.log("online");
});

通过 NetworkInformation API 获取更多网络相关信息

NetworkInformation API (opens in a new tab) 中提供了更多网络状态相关的信息,如最大下行速度、网络连接类型等。

function getConnection() {
  const nav = navigator as any;
  if (!isObject(nav)) return null;
  return nav.connection || nav.mozConnection || nav.webkitConnection;
}
 
function getConnectionProperty(): NetworkState {
  const c = getConnection();
  if (!c) return {};
  return {
    rtt: c.rtt,
    type: c.type,
    saveData: c.saveData,
    downlink: c.downlink,
    downlinkMax: c.downlinkMax,
    effectiveType: c.effectiveType,
  };
}
属性描述类型
rtt当前连接下评估的往返时延number
type设备使用与所述网络进行通信的连接的类型bluetooth | cellular | ethernet | none | wifi | wimax | other | unknown
downlink有效带宽估算(单位:兆比特/秒)number
downlinkMax最大下行速度(单位:兆比特/秒)number
saveData用户代理是否设置了减少数据使用的选项boolean
effectiveType网络连接的类型slow-2g | 2g | 3g | 4g

更多信息参考:MDN NetworkInformation (opens in a new tab)

封装成一个 useNetwork 自定义 Hook

以下代码是 ahooks 中的 useNetwork 自定义 Hook 实现方式,其核心原理是通过以上的 navigator.onLinenavigator.connection 中提供的 API 进行分装的。

其他的自定义 Hooks 也有类似实现的封装。

import { useEffect, useState } from 'react';
import { isObject } from '../utils';
 
export interface NetworkState {
  since?: Date;
  online?: boolean;
  rtt?: number;
  type?: string;
  downlink?: number;
  saveData?: boolean;
  downlinkMax?: number;
  effectiveType?: string;
}
 
enum NetworkEventType {
  ONLINE = 'online',
  OFFLINE = 'offline',
  CHANGE = 'change',
}
 
function getConnection() {
  const nav = navigator as any;
  if (!isObject(nav)) return null;
  return nav.connection || nav.mozConnection || nav.webkitConnection;
}
 
function getConnectionProperty(): NetworkState {
  const c = getConnection();
  if (!c) return {};
  return {
    rtt: c.rtt,
    type: c.type,
    saveData: c.saveData,
    downlink: c.downlink,
    downlinkMax: c.downlinkMax,
    effectiveType: c.effectiveType,
  };
}
 
function useNetwork(): NetworkState {
  const [state, setState] = useState(() => {
    return {
      since: undefined,
      online: navigator?.onLine,
      ...getConnectionProperty(),
    };
  });
 
  useEffect(() => {
    const onOnline = () => {
      setState((prevState) => ({
        ...prevState,
        online: true,
        since: new Date(),
      }));
    };
 
    const onOffline = () => {
      setState((prevState) => ({
        ...prevState,
        online: false,
        since: new Date(),
      }));
    };
 
    const onConnectionChange = () => {
      setState((prevState) => ({
        ...prevState,
        ...getConnectionProperty(),
      }));
    };
 
    window.addEventListener(NetworkEventType.ONLINE, onOnline);
    window.addEventListener(NetworkEventType.OFFLINE, onOffline);
 
    const connection = getConnection();
    connection?.addEventListener(NetworkEventType.CHANGE, onConnectionChange);
 
    return () => {
      window.removeEventListener(NetworkEventType.ONLINE, onOnline);
      window.removeEventListener(NetworkEventType.OFFLINE, onOffline);
      connection?.removeEventListener(NetworkEventType.CHANGE, onConnectionChange);
    };
  }, []);
 
  return state;
}
 
export default useNetwork;

useNetwork 自定义 Hook 使用方式

import React from 'react';
import { useNetwork } from 'ahooks';
 
export default () => {
  const networkState = useNetwork();
 
  return (
    <div>
      <div>Network information: </div>
      <pre>{JSON.stringify(networkState, null, 2)}</pre>
    </div>
  );
};

参考链接

2024 © OXXD.RSS