注册
web

浏览器检测之趣事

1 那段历史

在开发过程中,我们通常用用户代理字符串—浏览器端 window.navigator.userAgent或者服务器端header携带的user-agent —来用于检测当前浏览器是否为移动端, 比如:

if(isMobile()) {
// 移动端逻辑...
}

function isMobile () {
  const versions = (function () {
      const u = window.navigator.userAgent // 服务器端:req.header('user-agent')
      return {
        trident: u.indexOf('Trident') > -1, // IE内核
        presto: u.indexOf('Presto') > -1, // opera内核
        webKit: u.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核
        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1, // 火狐内核
        mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
        android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, // android终端或者uc浏览器
        iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器
        iPad: u.indexOf('iPad') > -1, // 是否iPad
        webApp: u.indexOf('Safari') === -1
      }
  }())
  return versions.mobile || versions.ios || versions.android || versions.iPhone || versions.iPad
}

我在使用时心里一直有疑问,一个移动端,为什么要做那么多判断呢?

目前我的 Chrome 浏览器:

ac3ac79bec78b6c0672b4a4f8e737553.png

看到这么一长串字符串,我表示更懵逼, Mozilla不是firefox的厂商么?这是 Chrome 浏览器,又怎么会有 “Safari” 的关键字?那个 “like Gecko” 又是什么鬼?

于是抱着这些疑问, 我打算好好深入了解一下浏览器检测这部分,没想到在学习过程中发现了挺有意思的事情,待我慢慢道来,大家也听个乐呵。

首先始于客户端与服务器端通信,要求携带名称与版本信息,于是服务器端与客户端协定好在每个HTTP请求的头部加上用户代理字符串(userAgent),方便服务器端进行检测,检测通过之后再进行后续操作。

早期的用户代理字符串(userAgent)很简单, 就 "产品名称/产品版本号",比如:"Mosaic/0.9"。93年之后,网景公司发布的Netscape Navigator 系列浏览器渐渐成为了当时最受欢迎的浏览器,于是它拥有了规则制定权,说从此以后我的用户代理字符串就为:

31fc7c0ed4e441d1984818e1c7834bc1.png

这时肯定有人会问,"Mozilla" 是网景公司为 Netscape 浏览器定义的代号,既然站在“食物链”顶端,那当然得用自己的命名,这能理解。可为啥直到现在,大部分主流浏览器的用户代理字符串(userAgent),第一个名称也是 “Mozilla” 呢?

这就是我即将要讲的, 第一根搅屎棍——微软。

96年,微软推出了 IE3, 而当时 Netscape Navigator3 的市场占有率太高,微软说,为了兼容 Netscape Navigator3, IE的用户代理字符串从此就为:

d22bfcb91df14485f96620871e8019b1.png

看到没有, 第一个名称还是 “Mozilla”,这个误导信息可以直接骗过服务器检测,而真正的 IE 版本放到后面去了。

大概意思就是初出茅庐的IE小同学怕自己知名度太低,万一服务端检测不到自己,用户流失了怎么办?隔壁老大哥家大业大,那就干脆去蹭波流量吧。关键是蹭流量就蹭流量吧,还嘴硬说我这可是Mozilla/2.0哦,不是Mozilla/3.0哦,跟那个Netscape Navigator3 不能说没有关系,只能说毫不相干。于是,IE成功地将自己伪装成了 Netscape Navigator。

这在当时来说是有争议,但不得不说, 微软这波操作相当精准。精准到直到97年 IE4 发布时,IE 的市场份额大幅增加,有了点话语权,也不藏着掖着了, 就跟 Netscape 同时将版本升级到了 Mozilla/4.0, 之后就一直保持同步了。

看到 IE 这波操作,场外观众有点坐不住了,更多的浏览器厂商沿着IE的老路,蹭着 Netscape 的流量,在此基础上依葫芦画瓢地设定自己的用户代理字符串(userAgent)。直到 Gecko 渲染引擎 (firefox的核心) 开始大流行,用户代理字符串(userAgent)基本已经形成了一个比较标准格式,服务端检测也能识别到 “Mozilla”、“Geoko” 等关键字,与之前字符串相比, 还增加了引擎、语言信息等等。

c6a4c52d15eb3ea47fd1d5fa752402d1.png

接下来我要说第二根搅屎棍——苹果。

2003年,苹果发布了 Safari, 它说,我的浏览器用户代理字符串是这样的:

0b1f715efc2772ae4199ee53a61b2852.png

Safari 用的渲染引擎是WebKit, 不是Gecko,它的核心是在渲染引擎KHTML基础上进行开发的,但是当时大部分浏览器的用户代理字符串(userAgent)都包含了 “Mozilla”、“Gecko”等关键字供服务器端检测。

苹果昂着脸,维持着表面的高傲,表示我的 WebKit 天下无敌、傲视群雄, 心里却颤颤发抖,小心翼翼地在用户代理字符串里加了个“like Gecko”,假装我是Gecko ?!

这波操作可谓是又当又立的典范!

我想可能心理阴影最大的要属 Netscape 了,本来 IE 来白嫖一波也就算了,你Safari 也要来,而且本身苹果的影响力就不容小觑,你再进来插一脚,让我以后怎么生存?但苹果说:“Safari 与 Mozilla 兼容,不能让网站以为用户使用了不受支持的浏览器而把 Safari 排斥在外。”大概意思是,我就是要白嫖, 怎么样?可以说是相当不要脸了。

不过至少苹果还有点藏着掖着, 而 Chrome 就有点不讲武德,它说,成年人的世界不做选择, 我想要的我都要:

09b980449abcaa43678107ca4034fe3f.png

Chrome 的渲染引擎是 Blink , Javascript引擎是 V8, 但它的用户代理字符串(userAgent)中, 不仅包含了“Mozilla”、“like Gecko”,还包含了 “WebKit” 的引擎信息, 几乎把能嫖的都嫖了, 只多了一个 “Chrome” 名称和版本号,甚至都没有一个 “Blink” 关键字,节操碎了一地,简直触目惊心,令人叹为观止。

到这里就不得不提一嘴高冷的Opera,直到Opera 8,用户代理字符串(userAgent)一直都是 “Opera/Version (OS-or-CPU; Encryption [; language])”

Opera 一直给人一种世人皆醉我独清、出淤泥而不染的气概。到直到 Opera9 画风突然变了, 估计也是看到几个大厂商各种骚操作,有点绷不住了,也跑去蹭流量。心态虽然崩但高冷人设不能崩,我就是不走寻常路,于是秀了一波玄学操作,它搞了两套用户代理字符串(userAgent):

6df6e3f543f46c98bb75c6ac207b6149.png

场外观众表示有点看不懂, 蹭完 Firefox 又去蹭 IE,还得分开蹭,这哪是秀操作, 这可是秀智商啊!纵观浏览器发展的这几十年,大概就是长江后浪推前浪,后浪还没把前浪踩死在沙滩上,后后浪又踩过来的一段历史吧。就在这历史的溪流中,用户代理字符串(userAgent)也已经形成了一个比较标准的格式。

目前,各个浏览器的用户代理字符串(userAgent)始终包含着以下信息:

245fc1e8d418066997863348bc471242.png

至于后来移动端的 IOS 和 Andriod 基本的格式就成了:

d4afdbe02185d2304f57aa0a5064ef49.png

这里的Mobile可能是 “iphone”、“ipad”、“BlackBerry”等等,Andriod设备的OS-or-CPU通常都是“Andriod”或“Linux”。所以,回到开头的isMobile检测函数内部,一大堆的检测判断条件, 简直就是一粒粒历史尘埃的堆叠。

同时,本地Chrome浏览器输出:

b646d313952b1b970b32d93e24eab014.png

我也可以翻译一下,大概意思就是,白嫖的Mozilla/5.0 + Macintosh平台 + Mac OS操作系统 × 10_15_7版本白嫖的AppleWebKit引擎/537.36引擎版本号 (KHTML内核, like Gecko 假装我是Gecko) Chrome浏览器/浏览器版本号99.0.4844.84 白嫖的Safari/Sarari版本号537.36。

本人表示很精彩, 一个用户代理字符串犹如看了一场轰轰烈烈(巨不要脸)、你挣我夺(你蹭我蹭)的大戏!

2 第三方插件

接下来, 为懒人推荐几款用于浏览器检测的省事的第三方插件。

1、如果只是检测设备是否为手机端, 可以用 isMobile ,它支持在node端或浏览器端使用。

地址:https://github.com/kaimallea/isMobile

2、如果要检测设备的类型、版本、CPU等信息,可以用 UAParser ,它支持在node端或浏览器端使用。

地址:https://github.com/faisalman/ua-parser-js

3、vue插件,vue-browser-detect-plugin

地址:https://github.com/ICJIA/vue-browser-detect-plugin

4、react插件,react-device-detect

地址:https://github.com/duskload/react-device-detect

5、在不同平台,要在Html中设置对应平台的CSS,可以用 current-device

地址:https://github.com/matthewhudson/current-device

需要注意的是, 第三方插件虽好用, 但也要注意安全问题哦,之前 UAParser 就被曝出被遭遇恶意投毒,所以只是简单的检测尽量手写。

3 移动端与PC端分流

移动端与PC端分流,可以用 nginx 来操作, nginx 可以通过 $http_user_agent 直接拿到用户代理信息:

http { 
server {
    listen 80;
      server_name localhost;
      location / {
          root /usr/share/nginx/pc; #pc端代码目录
          if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {
          root /usr/share/nginx/mobile; #移动端代码目录
          }
      index index.html;
      }
}
}

来源:八戒技术团队

0 个评论

要回复文章请先登录注册