谈谈检测浏览器类型

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

核心: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结合正则表达式
  • 第三方库(最简单便捷)
  • 条件注释(老方法了,了解即可)
  • 判断特性(最推荐最可靠的!)
复制代码
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) {
  // 记录数据到服务器的代码
}

完。

相关推荐
打小就很皮...9 天前
浏览器存储 Cookie,Local Storage和Session Storage
前端·缓存·浏览器
小妖66611 天前
chrome 浏览器怎么不自动提示是否翻译网站
浏览器
大名人儿14 天前
【浏览器网络请求全过程】
浏览器·网络请求·详解·全过程
windliang16 天前
Cursor 写一个网页标题重命名的浏览器插件
前端·浏览器
前端付豪16 天前
1、为什么浏览器要有渲染流程? ——带你一口气吃透 Critical Rendering Path
前端·后端·浏览器
啵啵学习17 天前
浏览器插件,提示:此扩展程序未遵循 Chrome 扩展程序的最佳实践,因此已无法再使用
前端·chrome·浏览器·插件·破解
前端南玖17 天前
通过performance面板验证浏览器资源加载与渲染机制
前端·面试·浏览器
mx95120 天前
真实业务场景:在React中使用Web Worker实现HTML导出PDF的性能优化实践
性能优化·浏览器
前端南玖24 天前
浏览器如何确定最终的CSS属性值?解析计算优先级与规则
前端·css·浏览器
NSJim24 天前
微软Edge浏览器字体设置
edge·浏览器·字体设置