前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数往知来:一次浏览器兼容工作中的知识点分析

数往知来:一次浏览器兼容工作中的知识点分析

作者头像
江米小枣
发布2020-06-15 15:14:57
9740
发布2020-06-15 15:14:57
举报
文章被收录于专栏:云前端云前端

在这个机器学习和人工智能遍地的年代,前端开发中的PC端浏览器兼容问题显得已经不是那么时髦和迫切了;刨去某些面向传统行业或网银支付等领域还不得不面对这个具体的问题外,大部分网站和移动端应用似乎可以潇洒的回避了;兼容工作的重点已经从几年前的样式统一转变为在PC端和移动端对新特性的支持和妥协,除了能更好更全面的满足用户,开发者了解优雅降级的兼容化思路,也是可以普遍应用在各项工作中的

开车!

开车!

开车!

项目构成


本次用来分析的项目,其package.json中的依赖大致如下:

代码语言:javascript
复制
"dependencies": {
   "bootstrap": "^3.3.7",
   "draft-js": "^0.10.1",
   "draftjs-to-html": "^0.7.4",
   "element-dataset": "^2.2.6",
   "form-serialize": "^0.7.1",
   "html-to-draftjs": "0.1.0-beta14",
   "immutable": "~3.7.4",
   "lodash": "^4.17.4",
   "mobx": "^3.1.9",
   "mobx-react": "^4.1.5",
   "moment": "^2.18.1",
   "react": "^15.6.1",
   "react-bootstrap": "^0.30.8",
   "react-datetime": "^2.8.9",
   "react-dom": "^15.6.1",
   "react-draft-wysiwyg": "^1.10.7",
   "react-router-dom": "^4.1.0",
   "native-promise-only": "^0.8.1",
   "whatwg-fetch": "^2.0.3"
}

显然,这是一个bootstrap样式的后台单页应用,用react实现了组件化、用mobx管理状态、引入了fetch等promise异步工具,并且使用了一些日期选择和富文本编辑器插件等第三方库

--- 感觉上IE就悬乎乎哒ㄟ( ▔, ▔ )ㄏ

目标用户

该产品为 toB 形态,主要面对部分可控的目标用户,大部分可以在指导下使用较新的chrome浏览器,但不排除一些用户使用firefox甚至IE的情况,所以针对该项目的主要目标就是让低版本IE用户处于“大部分特性可用、鼓励升级到chrome”的状况下,而不是回避甚至放弃这部分需求

兼容原则

  • 尽量不影响chrome等其他主流的浏览器
  • 最大化的尝试兼容已有功能
  • 对实在无法实现的功能降级处理
  • 对IE向下兼容到9(xp下可升级的最高版本)

顺藤摸瓜

这里我们以兼容后的index.html入口文件为切入点,梳理本次兼容过程的脉络:

代码语言:javascript
复制
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9,edge">
<title><%= htmlWebpackPlugin.options.title %></title>
<!--[if lte IE 9]>
<script src="CDN/polyfill.min.js"></script>
<script src="CDN/es5-shim.min.js"></script>
<script src="CDN/es5-sham.min.js"></script>
<script src="CDN/es6-shim.min.js"></script>
<script src="CDN/es6-sham.min.js"></script>
<script src="CDN/json3.min.js"></script>
<script src="CDN/classList.min.js"></script>
<script src="CDN/selectivizr-min.js"></script>
<script src="CDN/native.history.js"></script>
<script>
window.__defineGetter__(
   'history',
   function(){
       return History
   }
);
</script>
<![endif]-->
<script>
(function() {
var _isIE = /Trident\/(\d+)/i.exec(navigator.userAgent);
var _gteIE10 = _isIE && parseInt(_isIE[1])>5;
if (_gteIE10) {
   var s = document.createElement('script');
   document.head.appendChild(s);
   s.src = "CDN/es6-shim.min.js";
};
}())
</script>
</head>
<body>
   <!--[if lte IE 9]>
   <div id="oldIENoticeBox">
       您的浏览器过于老旧,
       请使用<a href="...">最新版chrome浏览器</a>
   </div>
   <![endif]-->
   <div id="root"></div>
</body>
</html>

使用X-UA-Compatible

“有时候需要限制Windows Internet Explorer在解析某个网页时使用特定的文档模式。使用X-UA-Compatible头部属性,可以让用户就像使用旧版本IE一样查看当前网页” -- MSDN

  • 使用X-UA-Compatible设置的被称为遗留文档模式(legacy document modes)
  • X-UA-Compatible不区分大小写,但必须出现在head中,且必须位于除title及其他meta元素外的元素前面
  • 服务器也可以通过配置指定X-UA-Compatible,但网页中的优先级高于服务器发送的
  • 可以设置其content值为诸如 IE9 或 EmulateIE9 之类的值;前者严格限制按照指定的版本渲染,而后者还会考虑!doctype的情况,从而有更好的兼容性
  • 设置content为edge则将Internet Explorer置于其支持的最高级模式之下
  • 可以设置多个值,比如content="IE=7,9,10",IE将从中选中自身能支持的最高版本
  • 如果content值中包含chrome=1,则表示支持Google Chrome Frame外挂插件(在IE外观下调用chrome内核浏览的挖墙脚插件;相应的也有个IETab用来在chrome/firefox下调用IE页面?)

判断真实的IE版本

  • 使用X-UA-Compatible设置遗留文档模式后,会带来新的问题,那就是 navigator.userAgent 返回的 MSIE 版本都是被模拟的值,而真实的浏览器版本难以判断了
  • 对于IE8以上,userAgent中包含了Trident内核的版本,可以用来判断真实版本
  • 对应关系为 `Trident/7.0` IE11 `Trident/6.0` IE10 `Trident/5.0` IE9 `Trident/4.0` IE8

IE的条件注释

“条件注释 (conditional comment) 是于HTML源码中被 Microsoft Internet Explorer 有条件解释的语句。条件注释可被用来向 Internet Explorer 提供及隐藏代码” -- wiki

IE中有两种特有的条件注释:HTML条件注释 和 JScript条件注释

HTML条件注释

语法为 <!--[if expression]> HTML <![endif]-->

  • 条件注释最初于微软的 Internet Explorer 5浏览器中出现,直至 IE10 停止支持
  • 对于非IE浏览器,被当作普通注释而忽略
代码语言:javascript
复制
举例:<!--[if IE 5]><p>欢迎来到IE5!</p><![endif]--><!--[if IE 5.0002]><p>欢迎来到Win2000中的IE5!</p><![endif]--><!--[if lt IE 5.5]><p>小于IE5.5</p><![endif]--><!--[if lte IE 6]><p>小于等于IE6</p><![endif]--><!--[if gt IE 6]><p>大于IE6</p><![endif]--><!--[if gte IE 6]><p>大于等于IE6</p><![endif]--><!--[if !(IE 7)]><p>不等于IE7</p><![endif]--><!--[if (gt IE 5)&(lt IE 8)]><p>大于IE5且小于IE8</p><![endif]--><!--[if (IE 6)|(IE 7)]><p>IE6或IE7</p><![endif]-->

下层显示(downlevel-revealed)的HTML条件注释

如下是一个“下层显示”条件“注释”的示例,它除了误导向的名字之外,根本不是一个 (X)HTML 注释,使用默认的微软语法:

代码语言:javascript
复制
<![if !IE]>
<link href="non-ie.css" rel="stylesheet">
<![endif]>

微软承认这种句法不是标准化的标记,其意图是这些标记被其它浏览器忽视并暴露其中的内容

JScript条件注释

关于JScript:

  • JScript是由微软公司开发的活动脚本语言,是微软对ECMAScript规范的实现。JScript最初是随IE3.0于1996年8月发布
  • IE 6-7 支持的 JScript5,以及IE8支持的JScript6,大致相当于 ECMAScript 3 / JavaScript 1.5
  • JScript最新的版本是基于尚未定稿的ECMAScript4.0版规范的JScript .NET,并且可以在微软的.Net环境下编译。JScript在ECMA的规范上增加了许多特性
  • JScript、JavaScript,以及Flash开发中的ActionScript等,都是ECMA的实现,可以认为是几种方言

自 Internet Explorer 4 开始,存在一种于 JScript 之中加入条件注释的类似的专有的机理,名称是条件编译:

代码语言:javascript
复制
<script>
/*@cc_on
 document.write("You are using IE4 or higher");
@*/
</script>

预变量 @_jscript_version

代码语言:javascript
复制
<script>
/*@cc_on @if (@_jscript_version == 10)
   document.write("You are using IE10"); @elif (@_jscript_version == 9)
   document.write("You are using IE9");
   
 @elif (@_jscript_version == 5.8)
   document.write("You are using IE8");
   
 @elif (@_jscript_version == 5.7 && window.XMLHttpRequest)
   document.write("You are using IE7"); @elif (@_jscript_version == 5.6
   || (@_jscript_version == 5.7 && !window.XMLHttpRequest))
   document.write("You are using IE6"); @elif (@_jscript_version == 5.5)
   document.write("You are using IE5.5"); @else
   document.write("You are using IE5 or older"); @end@*/
</script>
  • IE11 Standards mode 和 Windows 8.x Store apps 中不支持
  • IE10及更早版本的Standards mode中都支持

结合两种注释的识别IE10奇技淫巧

代码语言:javascript
复制
<!--[if !IE]><!--><script>
if (/*@cc_on!@*/false) {
   document.documentElement.className+=' ie10';
}
</script><!--<![endif]-->
  • 姥姥不疼:IE6-9发现了HTML条件注释但返回了false
  • 舅舅不爱:IE11两种注释都不认
  • IE10同时满足两种注释的交集

shim / sham / polyfill

这3个古怪的单词一般都用来描述一些给浏览器打补丁的第三方库

简单的说,他们的作用和区别是:

  • 一个shim是一个库,它将一个新的API引入到一个旧的环境中,而且仅靠旧环境中已有的手段实现。有时候也称为shiv
  • shim也无法被完美模拟的方法,就由sham尽量去模拟。sham只承诺你用的时候代码不会崩溃
  • 一个polyfill就是一段代码(或者插件),提供了那些开发者们希望浏览器原生提供支持的功能。因此,一个polyfill就是一个用在浏览器API上的特殊shim

词源考:shim sham

发端于20世纪30年代非裔美国人社区的一种踢踏舞。流传度非常高,以至于有人说几乎所有踢踏舞(tap)和摇摆舞(swing)舞者都会跳,是“踢踏舞/摇摆舞中的国际歌”

视频内容

另一个非常有感染力的版本:http://v.youku.com/v_show/id_XMTU5ODgyMTY2NA.html

一个中文教学视频:http://my.tv.sohu.com/us/275736703/82663464.shtml

词源考:polyfill

英国有一种品牌为Polyfilla的墙面填料,这种填料在美国叫Spackling Paste(Spackle是美国抹墙粉的一个品牌)-- 也就是我们一般叫做“腻子”或“填泥”的东西(对应的英文单词是putty和filler)

polyfill的作者正是英国人,他把浏览器想象成有裂缝的墙面,而用腻子可以把这些裂缝填平,最后得到的是光滑的浏览器“墙面”

万能的某宝:

类似的常用单词还有用来表示变量中“张三李四”的foo bar等,其解释可见 http://blog.csdn.net/deargua/article/details/1633123

几个典型的补丁

  • es5-shim
    • Array.prototype.filter
    • Function.prototype.bind
    • Number.prototype.toPrecision
  • es5-sham
    • Object.create
  • es6-shim
    • String.prototype.startsWith
    • Array.from
    • Array.prototype.find
  • es6-sham
    • Function.prototype.name
  • json3
    • JSON.stringify
    • JSON.parse
  • history.js
    • History.pushState
    • History.replaceState

File API

本次难以兼容的正是HTML5 File API,简单的说就是:IE10及以下不支持FileReader,分别用以下措施应对:

  1. 取消表单中上传头像的本地预览功能
  2. 有上传头像的表单从ajax提交改为原生提交,并在后台接口兼容
  3. 取消富文本编辑器中的上传图片功能(PRD中没有特别提及,仅在UI图上出现,优先级不高)

History API

本项目中的路由是由react-router中的<BrowserRouter>负责的,其官网的介绍如下:

A that uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL

言简意赅,react-router中的页面跳转,其实就是封装了HTML5 history API,并反映在了由其重写过的historylocation两个对象中。

需要注意的是,historylocation两个对象是从组件的props中获得的 -- 并非window中默认的全局对象。

简单的说,手动实现跳转的流程就是:

  • history.push(path, [state])history.replace(path, [state])等实现url变化并传递参数
  • 在目标界面用location.state得到传递的参数

实际对应的HTML5 history API方法则是:

  • history.pushState()history.replaceState()
  • window.addEventListener("popstate", e=>e.state)

该项目中,引入了 https://github.com/browserstate/history.js/ 并做相关处理覆盖了window.history,从而实现了基本兼容IE9/10

总结

至于零零碎碎的 IE css hack ,或 classList 等,就不展开细说了;通过以上总结和梳理,发现了很多我们已经习以为常的用法背后的原理,以及一些技术的发展脉络,相信在以后的应用中,会对相关技术更加心中有数,也能在其他工作中,更合理的分析和取舍

参考资料

  • https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx
  • http://zcfy.cc/article/specifying-legacy-document-modes-internet-explorer-95.html
  • http://blog.csdn.net/z69183787/article/details/17437195
  • https://msdn.microsoft.com/en-us/library/ms537512(v=vs.85).aspx
  • https://zh.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%B3%A8%E9%87%8A
  • https://stackoverflow.com/questions/135203/whats-the-difference-between-javascript-and-jscript
  • https://docs.microsoft.com/en-us/scripting/javascript/reference/at-cc-on-statement-javascript
  • https://www.impressivewebs.com/ie10-css-hacks/
  • https://en.wikipedia.org/wiki/Trident%28layoutengine%29
  • http://www.easy-swing.be/en/catalog/7-swing-history/59-history-of-shim-sham/
  • https://www.douban.com/event/24601886/
  • http://www.aichengxu.com/other/3730486.htm
  • https://stackoverflow.com/questions/6599815/what-is-the-difference-between-a-shim-and-a-polyfill
  • https://www.v2ex.com/t/250434
  • http://blog.csdn.net/bugknightyyp/article/details/8840111
  • http://www.jitterbuzz.com/less7.html
  • https://code.tutsplus.com/tutorials/an-introduction-to-the-html5-history-api--cms-22160
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/GlobalObjects/Object/defineGetter_
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目构成
  • 目标用户
  • 兼容原则
  • 顺藤摸瓜
  • 使用X-UA-Compatible
  • 判断真实的IE版本
  • IE的条件注释
    • HTML条件注释
      • 下层显示(downlevel-revealed)的HTML条件注释
        • JScript条件注释
          • 结合两种注释的识别IE10奇技淫巧
          • shim / sham / polyfill
            • 词源考:shim sham
              • 词源考:polyfill
                • 几个典型的补丁
                • File API
                • History API
                • 总结
                • 参考资料
                相关产品与服务
                内容分发网络 CDN
                内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档