关于前端:ShowMeBug中如何科学的识别用户浏览器

7次阅读

共计 6464 个字符,预计需要花费 17 分钟才能阅读完成。

常常有用户在 ShowMeBug 在线面试过程中,应用一些奇怪的浏览器来关上链接进行视频面试,而后呈现一些音视频卡顿等问题。

屡次排查后发现产品的音视频性能一但应用了浏览器的 WebRTC 个性,就会对浏览器的要求特地高,如不在失常的浏览器反对范畴内,就容易呈现问题。所以咱们在检测这个问题时,通常有两种办法辨认出浏览器的型号和版本。

第一种办法 :应用 modernizr 来进行 feature detection,通过探测这些 API 是否存在,来判读是否能够应用 WebRTC,毕竟 WebRTC 是有浏览器 API 组成。

不过,这种办法不够谨严,WebRTC 是一项很新的技术,往年 1 月份才正式成为官网规范,但目前只反对部份浏览器,稳定性也还须要进一步增强。

The World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF) announced today that Web Real-Time Communications (WebRTC), which powers myriad services, is now an official standard, bringing audio and video communications anywhere on the Web.

往年 1 月,W3C 和 IETF 发表,为有数服务提供反对的 WebRTC 现已成为官网规范。

———— 2021.01.26

网络实时通信 (WebRTC) 扭转了通信格局;成为万维网联盟 (W3C) 建议书和多个互联网工程工作组 (IETF) 规范

显然,feature detection 不合乎咱们的利用场景,目前不满足咱们的需要,所以此办法 pass。

第二种办法 ,也是咱们目前采纳的,业内比拟通用的方法就是检测 User-Agent。

curl http://202.38.95.46:12001/ -H "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HEICORE/49.1.2623.213 Safari/537.36"

User-Agent 是什么

用户不能间接去互联网上获取信息,须要一个软件作为载体去代表用户的行为,这个软件就是 User-Agent(用户代理),浏览器就是一种典型的 User-Agent。

用户应用不同的软件去用对立的协定去做雷同的事件。这也是定义在 http 申请里的,每一条 http 申请肯定会携带 User-Agent 头。

网站的服务者能够通过 User-Agent 头来判断用户应用了什么浏览器,当然也能够依据 User-Agent 的内容来提供差异化的服务。

规范语法和历史

本来 User-Agent 浏览器的语法是很清晰的

User-Agent: <product> / <product-version> <comment>

User-Agent – HTTP | MDN

因为能够依据 User-Agent 的内容来提供差异化的服务,所以当年在浏览器大战期间,浏览器的实现各不相同。当年 Mozilla(Firefox 的前身)浏览器最强的,很多网站都只对 Mozilla 提供高质量的服务,起初有人把本人伪装成了 Mozilla(没错,就是 IE 先开始的)。从此 Mozilla/5.0 就变成了 User-Agent 的第一段。

History of the browser user-agent string

起初的浏览就在这下面一直裁减,就像明天这样:

Linux / Firefox

Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0

Mac OS / Safari

Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15

Chromium OS / Chrome

Mozilla/5.0 (X11; CrOS x86_64 13904.16.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.25 Safari/537.36

Windows / Edge

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4482.0 Safari/537.36 Edg/92.0.874.0

这就变成了去辨认 User-Agent 变得很艰难,目前的辨认基本上都是应用正则表达式,配合本人的 User-Agent 库来判断。

这方面的库有很多,比照很多后,这个库是比拟全的
ua-parser-js

目前简直所有的网站辨认浏览器都是 User-Agent 来判断,有两个接口:

前端有浏览器接口:

window.navigator.userAgent

后端能够通过浏览器的 http request header 来拿到 User-Agent

user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36

如何应用 User-Agent

在挪动端因为算力的问题,在有些老旧的处理器上也会呈现卡顿的状况,不过,咱们能够在浏览器里跑一下 Benchmark 来判断算力是否够用,挪动端的 User-Agent 会携带处理器信息(能够去查数据库来判断)

所以说,目前所进行的操作是: 检测用户浏览器 ——> 给予提醒,并在文档页显示支持性列表

最初,要通过定义的规定生成这样一个表格:

为了升高将来的保护老本要应用一套数据源,既能够检测,又能够在文档页生成列表

ua-parser-js 的返回后果的数据结构比拟迷信,间接复用这一数据结构

interface Result {
  ua: string;
  browser: {
    name: string | undefined;
    version: string | undefined;
  };
  device: {
    model: string | undefined;
    type: string | undefined;
    vendor: string | undefined;
  };
  engine: {
    name: string | undefined;
    version: string | undefined;
  };
  os: {
    name: string | undefined;
    version: string | undefined;
  };
  cpu: {architecture: string | undefined;};
}

这样咱们能够定义三条规定:

  • daily_list(某个测试版的浏览器)
  • white_list(测试过没有问题的浏览器)
  • black_list(已知有问题的浏览器)
// 存在优先级关系: daily_list > white_list > black_list
//
// https://www.npmjs.com/package/ua-parser-js
// @params: ua-parser-js.result
// @params: {<list_name>: <whiteList | blackList>}
// @return: string(list_name)
function browserDetect(ua, list) {const { daily_list, white_list, black_list} = list
  if (checkList(ua, daily_list)) return 'daily_list'
  if (checkList(ua, white_list)) return 'white_list'
  if (checkList(ua, black_list)) return 'black_list'
  return ''
}

基于这种数据结构,退出一种简略的语法。反对版本号判断,退出这个几种符号的反对:>, , =, <,

因为没有现成能够用的,所以要本人写一段判断

compare-versions

这样的话配置文件就能够这样写:config/browser.yml

# https://www.npmjs.com/package/ua-parser-js
#
# 本文件的语法是在这个库之上做的批改

# 白名单,齐全没有问题的版本
white_list:
  - browser:
      name: "Chrome"
      version: ">= 85.0.0.0"
  - browser:
      name: "Firefox"
      version: ">= 85.0.0.0"
  - browser:
      name: "Edge"
      version: ">= 45.0.0.0"
    device:
      type: "mobile"
    os:
      name: "Android"
      version: ">= 10.0"

black_list:
  # 老版本的 Edge 不反对
  - browser:
      name: "Edge"
      version: "< 80.0.0.0"
    os:
      name: "Windows"

  # 手机微信内置浏览器
  - browser:
      name: "WeChat"
    device:
      type: "mobile"

应用咱们产品的用户,上至本人编译浏览器本人用的极客,下至用微信内置浏览器的小白,
所以要在 beta 版、dev 版、canary 开发版、nightly 版等各种版本中给予提醒,检测 WebRTC 不稳固,就可能会有问题。

已经有人用了开发版的浏览器,面试中呈现音视频不稳固,直到他更新了的版本之后才恢复正常。

因为开发版浏览器并不会在 ua 外面携带 dev 的标识,只能通过版本号来判断。能够应用 caniuse-lite 的数据库,取出最新稳定版的的版本号,而后进行版本号比对。

然而 caniuse-lite 的数据库有 1.3M 如果间接应用,会打包整个数据库。这个体积的减少了太多,须要优化一下,所以采纳把查问后果打包成文件的方法,实际上真正有用的数据十分的少。

创立一个生成器,能够动态创建这个文件 latest_browser_list_generator.js

#!/usr/bin/env node

const browserslist = require('browserslist')
const fs = require('fs')

const list = {
  'firefox': true,
  'chrome': true,
  'edge': true,
}

const latest = browserslist("last 1 version").filter(i => list[i.split(' ')[0]])
fs.writeFileSync('latest_browser_list.js', `export default ${JSON.stringify(latest)}`)

之后定期执行这两个就能够了

  • npx browserslist@latest --update-db
  • node latest_browser_list_generator.js

当然,这个能够用 GitHub action 或 GitLab CI 来每周执行一次

除此之外,360 浏览器的检测办法

360 浏览器暗藏了本人的 UA。360 浏览器只有在拜访本人的网站(比方:360.cn)是才会在 UA 里携带 QIHU 360SE(360 平安浏览器)或 QIHU 360EE(360 极速浏览器)字段

360 平安浏览器和 360 极速浏览器的判断 – V2EX

看待国内浏览器(这个能够检测到 360)

GitHub – mumuy/browser: Useragent analysis tool. 浏览器分析判断工具 – 用户代理、操作系统信息

不过这个库的作者并没有提供能够间接应用包,只能把外围代码提取进去。

  // https://github.com/mumuy/browser/blob/4a50ee18cc76a5013dea3596bb33fbab9ed584c3/Browser.js#L111-L143
  if (_window.chrome) {let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
    if (_window.chrome.adblock2345 || _window.chrome.common2345) {match['2345Explorer'] = true
    } else if (_mime('type', 'application/360softmgrplugin') ||
      _mime('type', 'application/mozilla-npqihooquicklogin')
    ) {is360 = true} else if (chrome_version > 36 && _window.showModalDialog) {is360 = true} else if (chrome_version > 45) {is360 = _mime('type', 'application/vnd.chromium.remoting-viewer')
      if (!is360 && chrome_version >= 69) {is360 = _mime('type', 'application/hwepass2001.installepass2001') || _mime('type', 'application/asx')
      }
    }
  }

  // 修改
  if (match['Mobile']) {match['Mobile'] = !(u.indexOf('iPad') > -1)
  } else if (is360) {if (_mime('type', 'application/gameplugin')) {match['360SE'] = true
    } else if (
      _navigator &&
      typeof _navigator['connection'] !== 'undefined' &&
      typeof _navigator['connection']['saveData'] == 'undefined'
    ) {match['360SE'] = true
    } else {match['360EE'] = true
    }
  }

不过这里要留神:navigator.mimeTypes 曾经从 Web 规范中移除(兴许将来的某天这个办法就没法用了)

Navigator.mimeTypes – Web APIs | MDN

判断 360 的版本,是做了一个版本的对应关系

// https://github.com/mumuy/browser/blob/4a50ee18cc76a5013dea3596bb33fbab9ed584c3/Browser.js#L283-L292
function get360SEVersion(u) {let hash = { '86': '13.0', '78': '12.0', '69': '11.0', '63': '10.0', '55': '9.1', '45': '8.1', '42': '8.0', '31': '7.0', '21': '6.3'}
  let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
  return hash[chrome_version] || ''
}
function get360EEVersion(u) {let hash = { '86': '13.0', '78': '12.0', '69': '11.0', '63': '9.5', '55': '9.0', '50': '8.7', '30': '7.5'}
  let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
  return hash[chrome_version] || ''
}

最初

尽管咱们分享了如何迷信辨认检测用户浏览器的办法,然而为了网络环境的衰弱,为了不重蹈浏览器大战时的覆辙,呐喊大家不要针对特定浏览器提供差异化内容。

正文完
 0