这几年手机和网络已是大多数人生活中的必需品,其中有不少人,好比我家”超哥“,她每次到一个新的环境中通常开口都会来一句,”请问你家有 WIFI 么,密码是多少?“,相信不少人都有这样的经历。接下来,本文将介绍在前端如何实如今线或离线检测、获取网络信息、获取网络延迟和网络测速等内容,有兴趣的小伙伴赶忙学起来。javascript
在现代的浏览器中,能够经过 navigator.onLine
获取当前网络的在线状态,该属性会根据用户的网络在线状态返回 true 或 false。前端
navigator.onLine; // true(在线) navigator.onLine; // false(离线)
但在某些场景,除了须要获取当前的网络状态以外,咱们更但愿能监听网络状态的变化,针对这个需求咱们能够监听 window 对象的 online
和 offline
事件,具体代码以下:java
window.addEventListener('online', () => { // 网络恢复咯,😄~~ }); window.addEventListener('offline', () => { // 网络掉线咯,😢~~ });
下面咱们来看一个完整的示例,该示例会在页面中动态显示当前的网络状态:git
一、页面加载后监听网络变化github
window.addEventListener('load', () => { // 在页面加载后,设置正确的网络状态 navigator.onLine ? showStatus(true) : showStatus(false); // 开始监听网络状态的变化 window.addEventListener('online', () => { showStatus(true); }); window.addEventListener('offline', () => { showStatus(false); }); });
二、在页面中动态显示当前的网络状态npm
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_eventsegmentfault
在某些视频网站中,当用户在非 WIFI 状况下点播视频时,会展现一个友好的提醒,让用户确认是否在非 WIFI 的状况下播放视频。微信小程序
(图片来源 - https://www.bilibili.com/)api
要知足这个需求,咱们就须要获取用户当前的网络信息。在浏览器中,经过 navigator.connection
能够获取网络链接状态 NetworkInformation 对象。
NetworkInformation
对象提供有关设备正在使用的链接与网络进行通讯的信息,并提供了在链接类型更改时通知事件。NetworkInformation
接口不能被是实例化, 而是经过Navigator
的connection
属性进行访问,且该属性是只读的。
NetworkInformation 对象中有多个只读的属性,好比 type 和 downlink 属性。
返回设备正在与网络进行通讯的链接类型。 它将是如下值之一:
bluetooth
cellular
ethernet
none
wifi
wimax
other
unknown
返回下行网络速度,以 Mbps 为单位。
三、NetworkInformation.downlinkMax
返回基础链接技术的最大下行网络速度,以 Mbps 为单位。
四、NetworkInformation.effectiveType
返回链接的有效类型,好比 “slow-2g”,“2g”,“3g” 或 “4g”。使用最近观察到的往返时间和下行链路值的组合来肯定此值。
表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便当即发送确认,不包含数据传输时间)总共经历的时间。
若是用户在用户代理上设置了减小数据使用量选项,则返回 true。
若须要监听网络信息的变化,能够经过 NetworkInformation.onchange 的方式来绑定监听函数,当网络信息发生改变时,会自动触发 change 事件,而后执行对应的监听函数。
介绍完上述的知识后,咱们来看个检测是否 WIFI 环境的示例代码:
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 的兼容性不是很好。
(图片来源 - https://caniuse.com/ - 2020/01/23)
针对这种状况,咱们能够根据当前的平台使用对应的 JS SDK 或安装对应的网络插件。下面咱们介绍微信、企业微信、微信小程序、钉钉和 cordova 等平台获取网络信息的方式。
微信/微信小程序/企业微信
wx.getNetworkType({ success: function (res) { var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi } });
钉钉
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 这个插件来获取网络信息。
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 (Packet Internet Groper), 因特网包探索器,用于测试网络链接量的程序。Ping是工做在 TCP/IP网络体系结构中应用层的一个服务命令, 主要是向特定的目的主机发送 ICMP(Internet Control Message Protocol 因特网报文控制协议) Echo 请求报文,测试目的站是否可达及了解其有关状态。
在 Web 环境中,若是要实现 Ping 的功能,咱们可使用 Github 上 Ping.js 这个 JavaScript 库。Ping.js 是一个小型且简单的 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 方法的具体实现以下:
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 请求,具体以下图所示:
在前端要实现网络测速,好比计算下行带宽,通常有如下几种方法:
navigator.connection.downlink
直接获取网速。下面咱们来重点分析一下以上几种方案的优缺点和具体实现。
该方案经过建立 XMLHttpRequest 对象并记录开始时间,而后发起 AJAX 请求,当请求成功后获取 'Content-Length'
响应头来取得资源的大小并记录结束时间,最后计算下行带宽。
该方案的具体实现以下:
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 }); }
使用示例
getSpeedWithAjax('./speed.jpg') .then(speed => { console.log(speed); });
该方案的好处是测试的文件不必定是图片,且返回的数据量能灵活控制。很差的地方是存在跨域问题。
该方案是经过建立 Image 对象并记录开始时间,而后绑定 onload 回调函数,接着指定一个有效的图片地址,一旦图片加载完成就会触发 onload 回调函数,最后在回调函数中记录结束时间并计算下行带宽。
该方案的具体实现以下:
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 }); }
使用示例
getSpeedWithImg( "https://s2.ax1x.com/2019/08/13/mPJ2iq.jpg", 8.97 ).then(speed => { console.log(speed); });
该方案的优势是不会存在跨域问题,很差的地方是要求文件必须是图片且已知文件大小,文件大小不能灵活控制。使用该方案,若须要保证结果准确性,能够考虑进行屡次测试取平均值。
function getSpeedWithDnlink() { // downlink测算网速 const connection = window.navigator.connection; if (connection && connection.downlink) { return connection.downlink * 1024 / 8; } }
使用示例
getSpeedWithDnlink();
该方案的优势是直接调用浏览器提供的 API 接口,不须要提供任何参数。它的缺点是存在较大的兼容性问题,带宽查询不是实时的,具备分钟级别的时间间隔。
最后咱们再来介绍一种综合测速方案,即先尝试采用 navigator.connection.downlink
测速,若当前浏览器不支持的话,再采用屡次 AJAX 测速并求平均值。
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 相关文章,欢迎感兴趣的小伙伴订阅哈!