這幾年手機和網絡已是大多數人生活中的必需品,其中有不少人,好比我家」超哥「,她每次到一個新的環境中通常開口都會來一句,」請問你家有 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 相關文章,歡迎感興趣的小夥伴訂閱哈!