前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在前端 Network 还能这样玩

在前端 Network 还能这样玩

作者头像
阿宝哥
发布2020-02-14 14:23:18
1.2K0
发布2020-02-14 14:23:18
举报
文章被收录于专栏:全栈修仙之路全栈修仙之路

明天就除夕了,现在全国 新型冠状病毒 2019-nCoV 的疫情还在不断扩大,84 岁的钟老和大批医务工作者还奋战在一线,相信很多国人新年的愿望就是疫情得到有效的控制,确诊的病人都能早日康复,一起迎接新的一年。武汉加油!中国加油!

这几年手机和网络已经是大多数人生活中的必需品,其中有很多人,比如我家”超哥“,她每次到一个新的环境中一般开口都会来一句,”请问你家有 WIFI 么,密码是多少?“,相信很多人都有这样的经历。接下来,本文将介绍在前端如何实现在线或离线检测、获取网络信息、获取网络延迟和网络测速等内容,有兴趣的小伙伴赶紧学起来。

一、在线或离线检测

在现代的浏览器中,可以通过 navigator.onLine 获取当前网络的在线状态,该属性会根据用户的网络在线状态返回 true 或 false。

代码语言:javascript
复制
navigator.onLine; // true(在线) 
navigator.onLine; // false(离线)

但在某些场景,除了需要获取当前的网络状态之外,我们更希望能监听网络状态的变化,针对这个需求我们可以监听 window 对象的 onlineoffline 事件,具体代码如下:

代码语言:javascript
复制
window.addEventListener('online', () => {
  // 网络恢复咯,?~~
});

window.addEventListener('offline', () => {
  // 网络掉线咯,?~~
});

下面我们来看一个完整的示例,该示例会在页面中动态显示当前的网络状态:

1、页面加载后监听网络变化

代码语言:javascript
复制
window.addEventListener('load', () => {
  // 在页面加载后,设置正确的网络状态
  navigator.onLine ? showStatus(true) : showStatus(false);

  // 开始监听网络状态的变化
  window.addEventListener('online', () => {
    showStatus(true);
  });

  window.addEventListener('offline', () => {
    showStatus(false);
  });
});

2、在页面中动态显示当前的网络状态

代码语言:javascript
复制
function showStatus(online) {
  const statusEl = document.querySelector('.network-status');

  if (online) {
    statusEl.classList.remove('warning');
    statusEl.classList.add('success');
    statusEl.innerText = `You're online! ?`;
  } else {
    statusEl.classList.remove('success');
    statusEl.classList.add('warning');
    statusEl.innerText = `You're offline! ?`;
  }
}

浏览器兼容情况: navigator.onLine —— https://caniuse.com/#search=navigator.onLine online event —— https://caniuse.com/#feat=mdn-api_window_online_event

二、获取网络信息

在某些视频网站中,当用户在非 WIFI 情况下点播视频时,会展示一个友好的提醒,让用户确认是否在非 WIFI 的情况下播放视频。

no-wifi-hint
no-wifi-hint

(图片来源 - https://www.bilibili.com/)

要满足这个需求,我们就需要获取用户当前的网络信息。在浏览器中,通过 navigator.connection 可以获取网络连接状态 NetworkInformation 对象。

NetworkInformation 对象提供有关设备正在使用的连接与网络进行通信的信息,并提供了在连接类型更改时通知事件。NetworkInformation 接口不能被是实例化, 而是通过 Navigatorconnection 属性进行访问,且该属性是只读的。

NetworkInformation 对象中有多个只读的属性,比如 type 和 downlink 属性。

1、NetworkInformation.type

返回设备正在与网络进行通信的连接类型。 它将是以下值之一:

  • bluetooth
  • cellular
  • ethernet
  • none
  • wifi
  • wimax
  • other
  • unknown

2、NetworkInformation.downlink

返回下行网络速度,以 Mbps 为单位。

3、NetworkInformation.downlinkMax

返回基础连接技术的最大下行网络速度,以 Mbps 为单位。

4、NetworkInformation.effectiveType

返回连接的有效类型,比如 “slow-2g”,“2g”,“3g” 或 “4g”。使用最近观察到的往返时间和下行链路值的组合来确定此值。

5、NetworkInformation.rtt

表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认,不包含数据传输时间)总共经历的时间。

6、NetworkInformation.saveData

如果用户在用户代理上设置了减少数据使用量选项,则返回 true。

若需要监听网络信息的变化,可以通过 NetworkInformation.onchange 的方式来绑定监听函数,当网络信息发生改变时,会自动触发 change 事件,然后执行对应的监听函数。

介绍完上述的知识后,我们来看个检测是否 WIFI 环境的示例代码:

代码语言:javascript
复制
function isWifi() {
  try {
    let wifi = true;
    const ua = window.navigator.userAgent;
    const conn = window.navigator.connection;
    // 判断是否微信环境
    if (/MicroMessenger/.test(ua)) {
      if (ua.indexOf("WIFI") >= 0) {
        return true;
      } else {
        wifi = false;
      }
      // 判断是否支持navigator.connection
    } else if (conn) {
      wifi = conn.type === "wifi"
    }
    return wifi;
  } catch (e) {
    return false;
  }
}

虽然通过 navigator.connection 可以方便地获取当前的网络信息,不过很可惜目前该 API 的兼容性不是很好。

navigator-connection
navigator-connection

(图片来源 - https://caniuse.com/ - 2020/01/23)

针对这种情况,我们可以根据当前的平台使用对应的 JS SDK 或安装对应的网络插件。下面我们介绍微信、企业微信、微信小程序、钉钉和 cordova 等平台获取网络信息的方式。

微信/微信小程序/企业微信

代码语言:javascript
复制
wx.getNetworkType({
  success: function (res) {
    var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi
  }
});

钉钉

代码语言:javascript
复制
dd.device.connection.getNetworkType({
    onSuccess : function(data) {
      {
         // result值: wifi 2g 3g 4g unknown none
         // none表示离线
         result: 'wifi' 
      }
    },
    onFail : function(err) {}
});

cordova

对于 cordova 环境,可以通过安装 cordova-plugin-network-information 这个插件来获取网络信息。

代码语言:javascript
复制
function checkConnection() {
    var networkState = navigator.connection.type;
 
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';
 
    alert('Connection type: ' + states[networkState]);
}
 
checkConnection();

浏览器兼容情况: navigator.connection —— https://caniuse.com/#search=navigator.connection

三、获取网络延迟

在日常工作中,当遇到某个站点无法访问或网络连接超时的时候,我们经常会打开命令行,然后使用 ping 命令,ping 一下对应的站点。比如,ping 一下全球最大的同性交友平台:

ping-github
ping-github

PING (Packet Internet Groper),因特网包探索器,用于测试网络连接量的程序。Ping是工作在 TCP/IP网络体系结构中应用层的一个服务命令, 主要是向特定的目的主机发送 ICMP(Internet Control Message Protocol 因特网报文控制协议)Echo 请求报文,测试目的站是否可达及了解其有关状态。

在 Web 环境中,如果要实现 Ping 的功能,我们可以使用 Github 上 Ping.js 这个 JavaScript 库。Ping.js 是一个小型且简单的 JavaScript 库,用于使用纯 JavaScript 方式来获取指定主机的网络延迟时间。该库的使用示例如下:

代码语言:javascript
复制
const p = new Ping();
p.ping("https://github.com", function(err, data) {
  if (err) {
    console.log("error loading resource")
    data = data + " " + err;
  }
  document.getElementById("ping-github").innerHTML = data;
});

因为 JavaScript 本身并没有提供 ping 的实现,所以通过 ping.js 获取的结果并不能保证准确性。由于 AJAX 请求有跨域的限制,所以不能通过 AJAX 方式来实现。Ping.js 的实现方式是使用从任意主机加载 favicon.ico 图片来确认响应时间。若 favicon.ico 图片不存在,则会返回 error 字符串和响应时间。

ping 方法的具体实现如下:

代码语言:javascript
复制
Ping.prototype.ping = function(source, callback) {
    var self = this;
    self.wasSuccess = false;
    self.img = new Image();
    self.img.onload = onload;
    self.img.onerror = onerror;

    var timer;
    var start = new Date();

    function onload(e) {
        self.wasSuccess = true;
        pingCheck.call(self, e);
    }

    function onerror(e) {
        self.wasSuccess = false;
        pingCheck.call(self, e);
    }

    if (self.timeout) {
       timer = setTimeout(function() {
         pingCheck.call(self, undefined);
    }, self.timeout); }

    /**
     * 计算响应时间并触发相应回调函数
     */
    function pingCheck() {
        if (timer) { clearTimeout(timer); }
        var pong = new Date() - start;

        if (typeof callback === "function") {
            if (!this.wasSuccess) {
                if (self.logError) { 
                  console.error("error loading resource"); 
                }
                return callback("error", pong);
            }
            return callback(null, pong);
        }
    }

    // 触发图片加载
    self.img.src = source + self.favicon + "?" + (+new Date()); 
};

对于上面的示例,执行 p.ping("https://github.com") 方法时,会发起一个 GET 请求,具体如下图所示:

ping-github-favicon
ping-github-favicon

四、网络测速

在前端要实现网络测速,比如计算下行带宽,一般有以下几种方法:

  1. 通过 AJAX 测算网速。
  2. 通过创建 Image 对象加载指定图片来测算网速。
  3. 通过 navigator.connection.downlink 直接获取网速。

下面我们来重点分析一下以上几种方案的优缺点和具体实现。

4.1 通过 AJAX 测算网速

该方案通过创建 XMLHttpRequest 对象并记录开始时间,然后发起 AJAX 请求,当请求成功后获取 'Content-Length' 响应头来取得资源的大小并记录结束时间,最后计算下行带宽。

该方案的具体实现如下:

代码语言:javascript
复制
function getSpeedWithAjax(url) {
    return new Promise((resolve, reject) => {
        let start = null;
        let end = null;
        start = new Date().getTime();
        const xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                end = new Date().getTime();
                const size = xhr.getResponseHeader('Content-Length') / 1024;
                const speed = size * 1000 / (end - start);
                resolve(speed);
            }
        }
        xhr.open('GET', url);
        xhr.send();
    }).catch(err => { throw err });
}

使用示例

代码语言:javascript
复制
getSpeedWithAjax('./speed.jpg')
 .then(speed => {
    console.log(speed);
});

该方案的好处是测试的文件不一定是图片,且返回的数据量能灵活控制。不好的地方是存在跨域问题。

4.2 通过创建 Image 对象加载指定图片来测算网速

该方案是通过创建 Image 对象并记录开始时间,然后绑定 onload 回调函数,接着指定一个有效的图片地址,一旦图片加载完成就会触发 onload 回调函数,最后在回调函数中记录结束时间并计算下行带宽。

该方案的具体实现如下:

代码语言:javascript
复制
function getSpeedWithImg(imgUrl, fileSize) {
    return new Promise((resolve, reject) => {
        let start = null;
        let end = null;
        let img = document.createElement('img');
        start = new Date().getTime();
        img.onload = function (e) {
            end = new Date().getTime();
            const speed = fileSize * 1000 / (end - start);
            resolve(speed);
        }
        img.src = imgUrl;
    }).catch(err => { throw err });
}

使用示例

代码语言:javascript
复制
getSpeedWithImg(
  "https://s2.ax1x.com/2019/08/13/mPJ2iq.jpg", 8.97
).then(speed => {
    console.log(speed);
});

该方案的优点是不会存在跨域问题,不好的地方是要求文件必须是图片且已知文件大小,文件大小不能灵活控制。使用该方案,若需要保证结果准确性,可以考虑进行多次测试取平均值。

4.3 通过 navigator.connection.downlink 直接获取网速
代码语言:javascript
复制
function getSpeedWithDnlink() {
    // downlink测算网速
    const connection = window.navigator.connection;
    if (connection && connection.downlink) {
        return connection.downlink * 1024 / 8;
    }
}

使用示例

代码语言:javascript
复制
getSpeedWithDnlink();

该方案的优点是直接调用浏览器提供的 API 接口,不需要提供任何参数。它的缺点是存在较大的兼容性问题,带宽查询不是实时的,具有分钟级别的时间间隔。

4.4 综合测速

最后我们再来介绍一种综合测速方案,即先尝试采用 navigator.connection.downlink 测速,若当前浏览器不支持的话,再采用多次 AJAX 测速并求平均值。

代码语言:javascript
复制
function getNetSpeed(url, times) {
    // downlink测算网速
    const connection = window.navigator.connection;
    if (connection && connection.downlink) {
        return connection.downlink * 1024 / 8;
    }
    // 多次测速求平均值
    const arr = [];
    for (let i = 0; i < times; i++) {
        arr.push(getSpeedWithAjax(url));
    }
    return Promise.all(arr).then(speeds => {
        let sum = 0;
        speeds.forEach(speed => {
            sum += speed;
        });
        return sum / times;
    })
}

备注:本章节的示例代码来源于 Github 上 network-speed-test 这个开源项目。

五、参考资源

欢迎小伙伴们订阅前端全栈修仙之路,及时阅读 Angular、TypeScript、Node.js/Java和Spring技术栈最新文章。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020/01/23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、在线或离线检测
  • 二、获取网络信息
  • 三、获取网络延迟
  • 四、网络测速
    • 4.1 通过 AJAX 测算网速
      • 4.2 通过创建 Image 对象加载指定图片来测算网速
        • 4.3 通过 navigator.connection.downlink 直接获取网速
          • 4.4 综合测速
          • 五、参考资源
          相关产品与服务
          云开发 CloudBase
          云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档