前幾天被問到如何檢測瀏覽器類型,我突然發現我對此并不了解,之前的項目中也沒有使用到過,只隱約記得通過一個自帶的方法即可獲取。所以今天特意來仔細補習一下。
核心:navigator.userAgent
1.正則表達式
2.引用外部庫
3.判斷瀏覽器的其他特性
一.首先講一下navigator.userAgent
navigator.userAgent
是一個包含用戶代理字符串(User Agent String)的屬性,這個字符串提供了關于用戶瀏覽器、操作系統和設備的信息。用戶代理字符串的生成和傳遞涉及瀏覽器和服務器之間的通信過程。
用戶代理字符串包含主要內容:
-用戶瀏覽器
-操作系統
-設備信息
接下來從構成、生成原理、傳遞解析和局限性幾個方面來講解一下用戶代理字符串:
1.1用戶代理字符串的構成
- 瀏覽器名稱和版本:標識瀏覽器的名稱和版本號。【這個內容導致經常用于判斷瀏覽器類型場景上!】
- 操作系統:標識用戶使用的操作系統及其版本。
- 渲染引擎:標識瀏覽器使用的渲染引擎。
- 設備信息:標識設備類型(比如手機、平板、桌面)。
舉個例子
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
我們來分析一下這個例子用戶代理字符串中包含了哪些信息呢
- Mozilla/5.0:兼容性標識符,表示兼容 Mozilla 的瀏覽器。
- (Windows NT 10.0; Win64; x64):操作系統信息,表示 Windows 10 64 位操作系統。
- AppleWebKit/537.36:渲染引擎,表示使用 WebKit 內核。
- (KHTML, like Gecko):表示兼容 Gecko 內核。
- Chrome/92.0.4515.107:瀏覽器名稱和版本號,表示 Chrome 92。
- Safari/537.36:表示兼容 Safari 537.36。【這里注意,正則判斷時,由于兼容性標識,也有可能chrome瀏覽器識別到safari字段】
1.2用戶代理字符串的生成
- 瀏覽器內部生成:
-
- 瀏覽器在初始化時,根據其內置的信息和用戶的操作系統,生成用戶代理字符串。
- 這些信息通常在瀏覽器的配置文件中定義,瀏覽器開發者在開發時設定。
- 用戶定制:
-
- 某些瀏覽器允許用戶通過設置或擴展自定義用戶代理字符串。用戶可以修改用戶代理字符串以偽裝成不同的瀏覽器或設備。【這一點導致了他的不可靠性】
1.3用戶代理字符串的傳遞(瀏覽器和服務端之間)
- HTTP 請求:
-
- 當用戶訪問網頁時,瀏覽器會發送 HTTP 請求到服務器。在請求頭中包含一個名為
User-Agent
的字段,攜帶用戶代理字符串。 - 示例 HTTP 請求頭:
- 當用戶訪問網頁時,瀏覽器會發送 HTTP 請求到服務器。在請求頭中包含一個名為
GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
- 服務器處理:
-
- 服務器接收到請求后,可以讀取
User-Agent
字段,解析用戶代理字符串以了解客戶端的瀏覽器、操作系統和設備信息。 - 基于這些信息,服務器可以返回相應的內容或進行特定的處理,如返回適配移動設備的網頁。
- 服務器接收到請求后,可以讀取
1.4用戶代理字符串的解析
知道了用戶代理字符串的內容之后,我們需要把他運用到實處,那么很重要的一點就是如何對其進行解析!
- 前端解析:
-
- 在前端 JavaScript 代碼中,可以通過
navigator.userAgent
獲取用戶代理字符串,并使用正則表達式或字符串匹配解析其中的信息。【這也是下一節要重點講的內容之一】 - 示例代碼:
- 在前端 JavaScript 代碼中,可以通過
const userAgent = navigator.userAgent;
console.log(userAgent); // 輸出用戶代理字符串
- 后端解析:
-
- 在服務器端,可以使用編程語言(如 Python、Java、Node.js)解析用戶代理字符串,進行用戶設備和瀏覽器的識別。
- 示例代碼(Node.js):
const http = require('http');http.createServer((req, res) => {const userAgent = req.headers['user-agent'];console.log(userAgent); // 輸出用戶代理字符串res.writeHead(200, {'Content-Type': 'text/plain'});res.end('Hello World\n');
}).listen(8080);
1.5用戶代理字符串的局限性
- 不可靠性:
-
- 用戶代理字符串可以被用戶修改或偽裝,因此基于其進行的判斷不總是可靠的。
- 某些瀏覽器或擴展允許用戶自定義用戶代理字符串,導致誤判。
- 復雜性和變化:
-
- 用戶代理字符串格式復雜且隨時間變化,不同瀏覽器和版本的格式可能不同。
- 解析用戶代理字符串需要持續更新解析規則,以應對新瀏覽器和版本的變化。
- 特性檢測優先:
-
- 現代 Web 開發推薦使用特性檢測,而不是依賴用戶代理字符串。特性檢測直接檢測瀏覽器是否支持特定功能,更加可靠。
- 示例代碼(特性檢測):
if ('geolocation' in navigator) {console.log('Geolocation is supported');
} else {console.log('Geolocation is not supported');
}
二.學習完用戶代理字符串,回到正題,如何判斷瀏覽器類型
- 正則表達式
- match結合正則表達式
- 第三方庫(最簡單便捷)
- 條件注釋(老方法了,了解即可)
- 判斷特性(最推薦最可靠的!)
2.1直接使用正則表達式匹配 navigator.userAgent
function getBrowserType() {const userAgent = navigator.userAgent;if (userAgent.indexOf("Firefox") > -1) {return "Firefox";} else if (userAgent.indexOf("SamsungBrowser") > -1) {return "Samsung Internet";} else if (userAgent.indexOf("Opera") > -1 || userAgent.indexOf("OPR") > -1) {return "Opera";} else if (userAgent.indexOf("Trident") > -1) {return "Internet Explorer";} else if (userAgent.indexOf("Edge") > -1) {return "Microsoft Edge";} else if (userAgent.indexOf("Chrome") > -1) {return "Chrome";} else if (userAgent.indexOf("Safari") > -1) {return "Safari";} else {return "Unknown";}
}console.log(getBrowserType());
navigator.userAgent
獲取了用戶代理字符串。indexOf
方法用于檢查字符串中是否包含特定的子字符串。如果包含,返回子字符串在字符串中的第一個索引,否則返回 -1。- 判斷不同瀏覽器標志來返回相應的瀏覽器類型。
這個方法簡單直觀,容易理解,是學習了用戶代理字符串后最容易想到的方法之一,但是卻不可靠,首先利用indexOf會存在大小寫敏感問題,其次這么多的ifelse也使得維護工作做起來很困難。
2.2 match()
結合正則表達式
function getBrowserType() {const userAgent = navigator.userAgent;if (userAgent.match(/Firefox/i)) {return "Firefox";} else if (userAgent.match(/SamsungBrowser/i)) {return "Samsung Internet";} else if (userAgent.match(/Opera|OPR/i)) {return "Opera";} else if (userAgent.match(/Trident/i)) {return "Internet Explorer";} else if (userAgent.match(/Edge/i)) {return "Microsoft Edge";} else if (userAgent.match(/Chrome/i)) {return "Chrome";} else if (userAgent.match(/Safari/i)) {return "Safari";} else {return "Unknown";}
}console.log(getBrowserType());
match()
方法在字符串中執行一個搜索匹配,并返回匹配結果的數組,如果沒有找到匹配,則返回null。- 正則表達式中的
i
標志表示不區分大小寫。(規避了直接使用indexOf大小寫敏感問題)
這個方法其實和第一種差不多,而且正則表達式提供了更強的匹配能力,處理起來更靈活,但是大部分人對于正則表達式還是做不到說寫就寫的,所以對于不熟悉正則表達式的開發者可能有一定的學習成本。
2.3使用第三方庫(如 bowser
)
import Bowser from "bowser";function getBrowserType() {const browser = Bowser.getParser(window.navigator.userAgent);return browser.getBrowserName();
}console.log(getBrowserType());
Bowser
是一個 JavaScript 庫,用于解析用戶代理字符串,并提供 API 來獲取瀏覽器和操作系統信息。Bowser.getParser(userAgent)
創建一個解析器對象。parser.getBrowserName()
返回瀏覽器名稱。
這種方法應該是很多人會選擇使用的,直接使用第三方庫,減少了自行編寫和維護代碼的工作量,而且庫本身就提供了豐富的功能和良好的兼容性。但是呢顯而易見需要引入額外的第三方庫,也是增加了項目的依賴。
2.4使用條件注釋(了解即可)
<script>var isIE = false;/*@cc_on@if (@_jscript_version)isIE = true;@end@*/if (isIE) {console.log("Internet Explorer");} else {console.log("Not Internet Explorer");}
</script>
- 條件注釋是 Internet Explorer 的一種特性,允許在 HTML 注釋中包含條件代碼。
@cc_on
是開啟條件編譯的指令。@_jscript_version
是一個 JScript 變量,表示當前 JScript 引擎的版本。
- 這種方法僅適用于 Internet Explorer,且現代瀏覽器不再支持條件注釋。
2.5基于特性檢測(最準確最推薦!)
function isIE() {return !!window.ActiveXObject || "ActiveXObject" in window;
}
function isEdge() {return !isIE() && !!window.StyleMedia;
}
if (isIE()) {console.log("Internet Explorer");
} else if (isEdge()) {console.log("Microsoft Edge");
} else {console.log("Not Internet Explorer or Edge");
}
- 基于特性檢測是通過檢測特定瀏覽器的特性來判斷瀏覽器類型,而不是依賴于
userAgent
。 window.ActiveXObject
和"ActiveXObject" in window
用于檢測 Internet Explorer。window.StyleMedia
用于檢測 Microsoft Edge。
這個方更加可靠,不依賴userAgent
,避免了用戶代理字符串可能被篡改的問題。(規避了不可靠性),但是缺點也很明顯,需要知道每種瀏覽器特有的特性,增加了復雜性。
三、應用場景
對于我個人的項目經歷來說,之前是幾乎沒有用到過瀏覽器類型判斷的,所以,我還去了解了一下需要用到瀏覽器類型判斷的一些應用場景。
3.1 瀏覽器特性檢測和兼容性處理
最容易想到的第一個應用場景,就是對兼容性的處理。某些瀏覽器對特定的功能或 API 支持不一致。需要通過檢測用戶代理字符串,可以針對特定瀏覽器執行不同的代碼,來確保功能的兼容性。
const userAgent = navigator.userAgent;if (userAgent.match(/Trident/i)) {// 針對 Internet Explorer 的特定處理
} else if (userAgent.match(/Edge/i)) {// 針對 Microsoft Edge 的特定處理
} else if (userAgent.match(/Chrome/i)) {// 針對 Chrome 的特定處理
} else if (userAgent.match(/Safari/i)) {// 針對 Safari 的特定處理
}
3.2 響應式設計和設備檢測
第二個我想到的就是響應式,因為之前經常在項目中使用媒體查詢去實現響應式涉及,我們知道很多頁面在移動設備和桌面設備之間是不同的布局和功能。所以我們通常需要通過檢測用戶代理字符串,確定用戶是否在使用移動設備,從而去調整頁面布局和交互方式。
const userAgent = navigator.userAgent;if (/Mobi|Android/i.test(userAgent)) {// 針對移動設備的布局和交互處理
} else {// 針對桌面設備的布局和交互處理
}
3.3 分析和統計
對于一些數據埋點,有時候可能還需要考慮到觸發時所在的環境,在網站分析和統計中,收集用戶代理字符串信息可以幫助開發者了解用戶的瀏覽器和設備使用情況,這個對于開發者是很有好處的。可以作為性能優化的量化依據。
// 將用戶代理字符串發送到分析服務器
sendUserAgentToAnalytics(navigator.userAgent);function sendUserAgentToAnalytics(userAgent) {// 發送數據到服務器的代碼
}
還有一些收集來的運用場景,自己想可能不太會想的到,不過看完之后就會覺得確實了:
3.4. 功能降級
某些情況有些特定功能可能不能再在某些瀏覽器使用。也可以通過檢測用戶代理字符串,·去選擇替代方案。
再說直白點就比如很多用戶使用很老的版本的設備或者未更新系統之類的,這里就可以運用到。
const userAgent = navigator.userAgent;if (userAgent.match(/MSIE|Trident/i)) {// 提供替代方案或警告用戶使用現代瀏覽器alert("Your browser is not supported. Please upgrade to a modern browser.");
}
5. 動態加載資源
涉及到資源加載優化了,很多時候不同設備下的靜態資源是截然不同的,可以根據用戶的瀏覽器和設備類型,動態加載不同的資源(如 CSS、JavaScript 文件),以優化性能和用戶體驗。
<script>const userAgent = navigator.userAgent;if (/Mobi|Android/i.test(userAgent)) {// 加載移動設備的樣式表document.write('<link rel="stylesheet" href="mobile.css">');} else {// 加載桌面設備的樣式表document.write('<link rel="stylesheet" href="desktop.css">');}
</script>
6. 調試和日志記錄
在調試和日志記錄中,記錄用戶代理字符串可以幫助開發者了解用戶的瀏覽器環境,從而更快地定位和解決問題。
console.log("User Agent: " + navigator.userAgent);// 將用戶代理字符串記錄到日志服務器
logUserAgent(navigator.userAgent);function logUserAgent(userAgent) {// 記錄數據到服務器的代碼
}
完。