当前位置: 首页 > news >正文

谈谈检测浏览器类型

前几天被问到如何检测浏览器类型,我突然发现我对此并不了解,之前的项目中也没有使用到过,只隐约记得通过一个自带的方法即可获取。所以今天特意来仔细补习一下。

核心:navigator.userAgent

1.正则表达式

2.引用外部库

3.判断浏览器的其他特性

一.首先讲一下navigator.userAgent

navigator.userAgent 是一个包含用户代理字符串(User Agent String)的属性,这个字符串提供了关于用户浏览器操作系统设备的信息。用户代理字符串的生成和传递涉及浏览器和服务器之间的通信过程。

用户代理字符串包含主要内容:

-用户浏览器

-操作系统

-设备信息

接下来从构成、生成原理、传递解析和局限性几个方面来讲解一下用户代理字符串:

1.1用户代理字符串的构成

  1. 浏览器名称和版本:标识浏览器的名称和版本号。【这个内容导致经常用于判断浏览器类型场景上!】
  2. 操作系统:标识用户使用的操作系统及其版本。
  3. 渲染引擎:标识浏览器使用的渲染引擎。
  4. 设备信息:标识设备类型(比如手机、平板、桌面)。
举个例子
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. 浏览器内部生成
    • 浏览器在初始化时,根据其内置的信息和用户的操作系统,生成用户代理字符串。
    • 这些信息通常在浏览器的配置文件中定义,浏览器开发者在开发时设定。
  1. 用户定制
    • 某些浏览器允许用户通过设置或扩展自定义用户代理字符串。用户可以修改用户代理字符串以伪装成不同的浏览器或设备。【这一点导致了他的不可靠性】

1.3用户代理字符串的传递(浏览器和服务端之间)

  1. HTTP 请求
    • 当用户访问网页时,浏览器会发送 HTTP 请求到服务器。在请求头中包含一个名为User-Agent的字段,携带用户代理字符串。
    • 示例 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
  1. 服务器处理
    • 服务器接收到请求后,可以读取User-Agent字段,解析用户代理字符串以了解客户端的浏览器、操作系统和设备信息。
    • 基于这些信息,服务器可以返回相应的内容或进行特定的处理,如返回适配移动设备的网页。

1.4用户代理字符串的解析

知道了用户代理字符串的内容之后,我们需要把他运用到实处,那么很重要的一点就是如何对其进行解析!

  1. 前端解析
    • 在前端 JavaScript 代码中,可以通过navigator.userAgent获取用户代理字符串,并使用正则表达式字符串匹配解析其中的信息。【这也是下一节要重点讲的内容之一】
    • 示例代码:
const userAgent = navigator.userAgent;
console.log(userAgent); // 输出用户代理字符串
  1. 后端解析
    • 在服务器端,可以使用编程语言(如 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用户代理字符串的局限性

  1. 不可靠性
    • 用户代理字符串可以被用户修改或伪装,因此基于其进行的判断不总是可靠的。
    • 某些浏览器或扩展允许用户自定义用户代理字符串,导致误判。
  1. 复杂性和变化
    • 用户代理字符串格式复杂且随时间变化,不同浏览器和版本的格式可能不同。
    • 解析用户代理字符串需要持续更新解析规则,以应对新浏览器和版本的变化。
  1. 特性检测优先
    • 现代 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) {// 记录数据到服务器的代码
}

完。

相关文章:

谈谈检测浏览器类型

前几天被问到如何检测浏览器类型&#xff0c;我突然发现我对此并不了解&#xff0c;之前的项目中也没有使用到过&#xff0c;只隐约记得通过一个自带的方法即可获取。所以今天特意来仔细补习一下。 核心&#xff1a;navigator.userAgent 1.正则表达式 2.引用外部库 3.判断浏…...

Django 和 Django REST framework 创建对外 API

1. 环境准备 确保你已经安装了 Python 和 Django。如果尚未安装 Django REST framework&#xff0c;通过 pip 安装它&#xff1a; pip install djangorestframework 2. 创建 Django 项目 如果你还没有 Django 项目&#xff0c;可以通过以下命令创建&#xff1a; django-ad…...

数据结构之“刷链表题”

&#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;数据结构 目录 前言 一、相交链表 题目链接 大致思路 代码实现 二、环形链表1 题目链接 大致思路 代码实现 三、环形链表2 题目链接 大致思路 代码实…...

复分析——第9章——椭圆函数导论(E.M. Stein R. Shakarchi)

第 9 章 椭圆函数导论 (An Introduction to Elliptic Functions) The form that Jacobi had given to the theory of elliptic functions was far from perfection; its flaws are obvious. At the base we find three fundamental functions sn, cn and dn. These functio…...

使用kubeadm安装k8s并部署应用

安装k8s 1. 准备机器 准备三台机器 192.168.136.104 master节点 192.168.136.105 worker节点 192.168.136.106 worker节点2. 安装前配置 1.基础环境 ######################################################################### #关闭防火墙&#xff1a; 如果是云服务器&…...

springMVC学习

概述 Spring MVC&#xff08;Model-View-Controller&#xff0c;模型-视图-控制器&#xff09;是Spring框架的一部分&#xff0c;用于构建基于Java的Web应用程序。它遵循MVC设计模式&#xff0c;分离了应用程序的不同方面&#xff08;输入逻辑、业务逻辑和UI逻辑&#xff09;&…...

深入探讨光刻技术:半导体制造的关键工艺

前言 光刻&#xff08;Photolithography&#xff09;是现代半导体制造过程中不可或缺的一环&#xff0c;它的精度和能力直接决定了芯片的性能和密度。本文将详细介绍光刻技术的基本原理、过程、关键技术及其在半导体制造中的重要性。 光刻技术的基本原理 光刻是一种利用光化…...

CesiumJS【Basic】- #042 绘制纹理线(Primitive方式)

文章目录 绘制纹理线(Primitive方式)1 目标2 代码2.1 main.ts3 资源文件绘制纹理线(Primitive方式) 1 目标 使用Primitive方式绘制纹理线 2 代码 2.1 main.ts var start = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);var...

代码随想录第38天|动态规划

1049. 最后一块石头的重量 II 参考 备注: 当物体容量也等同于价值时, 01背包问题的含义则是利用好最大的背包容量sum/2, 使得结果尽可能的接近或者小于 sum/2 等价: 尽可能的平分成相同的两堆, 其差则为结果, 比如 (abc)-d, (ac)-(bd) , 最终的结果是一堆减去另外一堆的和, 问…...

java生成excel,uniapp微信小程序接收excel并打开

java引包&#xff0c;引的是apache.poi <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency> 写一个测试类&#xff0c;把excel输出到指定路径 public s…...

sam_out 目标检测的应用

缺点参考地址训练验证模型解析 缺点 词表太大量化才可 参考地址 https://aistudio.baidu.com/projectdetail/8103098 训练验证 import os from glob import glob import cv2 import paddle import faiss from out_yolo_model import GPT as GPT13 import pandas as pd imp…...

VLAN原理与配置

AUTHOR &#xff1a;闫小雨 DATE&#xff1a;2024-04-28 目录 VLAN的三种端口类型 VLAN原理 什么是VLAN 为什么使用VLAN VLAN的基本原理 VLAN标签 VLAN标签各字段含义如下&#xff1a; VLAN的划分方式 VLAN的划分包括如下5种方法&#xff1a; VLAN的接口链路类型 创建V…...

使用Spring Boot实现RESTful API

使用Spring Boot实现RESTful API 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨如何利用Spring Boot框架实现RESTful API&#xff0c;这是现…...

中英双语介绍美国常春藤联盟( Ivy League):八所高校

中文版 常春藤联盟简介 常春藤联盟&#xff08;Ivy League&#xff09;是美国东北部八所私立大学组成的高校联盟。虽然最初是因体育联盟而得名&#xff0c;但这些学校以其学术卓越、历史悠久、校友杰出而闻名于世。以下是对常春藤联盟的详细介绍&#xff0c;包括其由来、成员…...

【计算机网络】常见的网络通信协议

目录 1. TCP/IP协议 2. HTTP协议 3. FTP协议 4. SMTP协议 5. POP3协议 6. IMAP协议 7. DNS协议 8. DHCP协议 9. SSH协议 10. SSL/TLS协议 11. SNMP协议 12. NTP协议 13. VoIP协议 14. WebSocket协议 15. BGP协议 16. OSPF协议 17. RIP协议 18. ICMP协议 1…...

java实现http/https请求

在Java中&#xff0c;有多种方式可以实现HTTP或HTTPS请求。以下是使用第三方库Apache HttpClient来实现HTTP/HTTPS请求的工具类。 优势和特点 URIBuilder的优势在于它提供了一种简单而灵活的方式来构造URI&#xff0c;帮助开发人员避免手动拼接URI字符串&#xff0c;并处理参…...

NC204871 求和

链接 思路&#xff1a; 对于一个子树来说&#xff0c;子树的节点就包括在整颗树的dfs序中子树根节点出现的前后之间&#xff0c;所以我们先进行一次dfs&#xff0c;用b数组的0表示区间左端点&#xff0c;1表示区间右端点&#xff0c;同时用a数组来标记dfs序中的值。处理完dfs序…...

git克隆代码warning: could not find UI helper ‘git-credential-manager-ui‘

git克隆代码warning: could not find UI helper ‘git-credential-manager-ui’ 方案 git config --global --unset credential.helpergit-credential-manager configure...

Generator 是怎么样使用的以及各个阶段的变化如何

Generators 是 JavaScript 中一种特殊类型的函数&#xff0c;可以在执行过程中暂停&#xff0c;并且在需要时恢复执行。它们是通过 function* 关键字来定义的。Generator 函数返回的是一个迭代器对象&#xff0c;通过调用该迭代器对象的 next() 方法来控制函数的执行。在调用 n…...

一文了解Java中 Vector、ArrayList、LinkedList 之间的区别

目录 1. 数据结构 Vector 和 ArrayList LinkedList 2. 线程安全 Vector ArrayList 和 LinkedList 3. 性能 插入和删除操作 随机访问 4. 内存使用 ArrayList 和 Vector LinkedList 5. 迭代器行为 ArrayList 和 Vector LinkedList 6. 扩展策略 ArrayList Vecto…...

Linux 常用命令语法总结

Linux 常用命令语法总结 1. 文件和目录操作 1.1 基本文件操作 # 列出文件和目录 ls # 列出当前目录内容 ls -l # 详细列表格式 ls -la # 显示隐藏文件 ls -lh # 人性化显示文件大小 ls...

Go 语言中的 make 函数详解

Go 语言中的 make 函数详解 make 是 Go 语言中的一个​​内置函数​​&#xff0c;用于​​初始化切片&#xff08;slice&#xff09;、映射&#xff08;map&#xff09;和通道&#xff08;channel&#xff09;​​这些引用类型。这些类型必须在使用前通过 make 初始化&#x…...

DuckDB + Spring Boot + MyBatis 构建高性能本地数据分析引擎

DuckDB 是一款令人兴奋的内嵌式分析型数据库 (OLAP)&#xff0c;它为本地数据分析和处理带来了前所未有的便捷与高效 &#x1f680;。它无需外部服务器&#xff0c;可以直接在应用程序进程中运行&#xff0c;并提供了强大的 SQL 支持和列式存储带来的高性能。 什么是 DuckDB&am…...

grafana-mcp-analyzer:基于 MCP 的轻量 AI 分析监控图表的运维神器!

还在深夜盯着 Grafana 图表手动排查问题&#xff1f;今天推荐一个让 AI 能“读图说话”的开源神器 —— grafana-mcp-analyzer。 想象一下这样的场景&#xff1a; 凌晨3点&#xff0c;服务器告警响起。。。你睁着惺忪的眼睛盯着复杂的监控图表 &#x1f635;‍&#x1f4ab;花…...

SQL Server 日期时间类型全解析:从精确存储到灵活转换

SQL Server 日期时间类型全解析&#xff1a;从精确存储到灵活转换 一、引言&#xff1a;日期时间处理的核心挑战 在数据管理中&#xff0c;日期时间类型是最常用却最容易出错的数据类型之一。不同业务场景对时间精度、时区感知、存储效率的需求差异极大&#xff1a; 金融交易…...

Python入门手册:异常处理

在编程过程中&#xff0c;异常处理是一个非常重要的环节。它可以帮助我们处理程序运行时可能出现的错误和异常情况&#xff0c;确保程序的稳定性和可靠性。Python提供了强大的异常处理机制&#xff0c;使得我们能够优雅地处理各种异常情况。今天&#xff0c;就让我们一起深入学…...

win32相关(消息Hook)

消息Hook 要想实现消息Hook需要使用到三个相关的Api SetWindowsHookEx // 设置钩子CallNextHookEx // 将钩子信息传递到当前钩子链中的下一个子程序UnhookWindowsHookEx // 卸载钩子 我们编写的消息钩子需要将设置钩子的函数写到dll里面&#xff0c;当钩住一个线程后&#xff…...

二.单例模式‌

一.单例模式的定义 单例模式是一种‌创建型设计模式‌&#xff0c;确保一个类‌只有一个实例‌&#xff0c;并提供该实例的‌全局访问点‌。 1.1.核心目标 唯一实例‌&#xff1a;限制类的实例化次数仅一次。‌全局访问‌&#xff1a;提供统一的访问入口&#xff08;通常是静…...

从零搭建uniapp项目

目录 创建uni-app项目 基础架构 安装 uni-ui 组件库 安装sass依赖 easycom配置组件自动导入 配置view等标签高亮声明 配置uni-ui组件类型声明 解决 标签 错误 关于tsconfig.json中提示报错 关于非原生标签错误&#xff08;看运气&#xff09; 安装 uview-plus 组件库…...

图片压缩工具 | 图片生成PDF文档

OPEN-IMAGE-TINY&#xff0c;一个基于 Electron VUE3 的图片压缩工具&#xff0c;项目开源地址&#xff1a;https://github.com/0604hx/open-image-tiny ℹ️ 需求描述 上一版本发布后&#xff0c;有用户提出想要将图片转换&#xff08;或者说生成更为贴切&#xff09;PDF文档…...