之前对浏览器兼容性问题只是大概知道一些点,没想到此次真正着手去作的时候,仍是碰到了不少问题。刚开始的时候一边解决问题,一边想着:用 IE8 的都是神经病,到后来,我发现完了,I LOVE IE。javascript
在此次作小蜜 PC 版的时候,因为早于 PC 版,无线版已经从新设计了全新版,作了不少架构上的优化调整。因此在作的时候把无线版的前端架构拿了过来,主要的考虑就是品牌和功能保持跟无线版统一的同时,技术上也可相互支持以及组件复用。php
无线版技术上主要采用 ES6 + Webpack + Babel 的方式,因为项目的独特性和特殊需求,并无使用任何框架,只引入 zepto 做为一个标准支撑库。html
而 PC 版的架构跟无线版基本保持一致,主要是把 zepto 换成了 jQuery。前端
下面是一些基本的开发依赖:java
{ "devDependencies": { "babel-core": "~6.3.15", "babel-loader": "~6.2.0", "babel-preset-es2015": "~6.3.13", "babel-preset-stage-0": "~6.3.13", "babel-runtime": "~6.3.13", "extract-text-webpack-plugin": "~0.9.1", "less-loader": "~2.2.1", "nunjucks-loader": "~1.0.7", "style-loader": "~0.10.2", "webpack": "~1.12.9", "webpack-dev-server": "^1.10.1" } }
因为 Babel 默认只转换转各类 ES2015 语法,而不转换新的 API,好比 Promise,以及 Object.assign、Array.from 这些新方法,这时咱们须要提供一些 ployfill 来模拟出这样一个提供原生支持功能的浏览器环境。webpack
主要有两种方式:babel-runtime
和 babel-polyfill
。git
babel-runtime 的做用是模拟 ES2015 环境,包含各类分散的 polyfill 模块,咱们能够在本身的模块里单独引入,好比 promise:github
import 'babel-runtime/core-js/promise'
它们不会在全局环境添加未实现的方法,只是这样手动引用每一个 polyfill 会很是低效,咱们能够借助 Runtime transform
插件来自动化处理这一切。web
首先使用 npm 安装:chrome
npm install babel-plugin-transform-runtime --save-dev
而后在 webpack 配置文件的 babel-loader 增长选项:
loader: ["babel-loader"], query: { plugins: [ "transform-runtime" ], presets: ['es2015', 'stage-0'] }
而 babel-polyfill
是针对全局环境的,引入它浏览器就好像具有了规范里定义的完整的特性,一旦引入,就会跑一个 babel-polyfill
实例。用法以下:
1.安装 babel-polyfill
npm install babel-polyfill --save
2.在入口文件中引用:
import 'babel-polyfill'
其实作到这些,在大部分浏览器就能够正常跑了,但咱们作的是一个用户环境很不肯定的产品,对一些年代久远但又不容忽视的运行环境,好比 IE8,咱们作的还不够。
接下来将开始讲述咱们在兼容性方面遇到的一些问题,和解决方法。
最开始作的时候并无针对 IE 作一些兼容性方面的处理,结果在 IE8 上一跑一堆问题。
第一步,咱们把 jQuery
换成 1.12.1 ,由于 2.X 已经再也不支持 IE8。
但并无像咱们想象中的那样,只是简单换一下 jQuery
版本就能够正常运行了。
这是遇到的第一个问题。在兼容性测试过程当中,对下面的代码:
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
或者这种:
module.exports = _main2.default;
在 IE8 下会直接报”缺乏标识符、字符串或数字”的错。
咱们得在对象的属性上加 ''
才能够。就像下面这样:
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } module.exports = _main2['default'];
至于缘由,并非 IE8 下对象的属性必须得加 ''
才行,而是 default
的问题,做为一个关键字,一样的问题还包括 catch
。
这两种状况,能够经过使用 transform-es3-property-literals
和 transform-es3-member-expression-literals
这两个插件搞定。
总之,在平时写代码的时候避免使用关键字,或者保留字做为对象的属性值,尤为是在习惯不加引号的状况下。相关讨论:Allow reserved words for properties
为了兼容像 IE8 这样的老版本浏览器,咱们引入 es5-shim
做为 polyfill。
但在遇到 Object.defineProperty
仍提示 "对象不支持此操做"
As currently implemented, the Object.defineProperty shim will not install on IE8 because IE8 already has such a method. However, the built-in IE8 method only works when applied to DOM objects.
其实 es5-shim 明确说明,这个方法的 polyfill 在 IE8 会失败,由于 IE8 已经有个同名的方法,但只是用于 DOM 对象。
一样的问题还包括 Object.create
,上述问题能够再引入 es5-sham 解决.
项目中有部分代码直接使用 addEventListener
这个 API,但在 IE8 下的事件绑定并非这个方法。
这个问题很容易解决,也无需去写额外的 polyfill。咱们已经把 jQuery 换成 1.x,因此只需把代码中 addEventListener
换成 jQuery
的写法就 Okay 了。
jQuery
其实为咱们封装了不少 API,并作了不少兼容性的封装,相似的只要使用封装好的就能够了。
这个问题是在特定场景下【转人工】出现的,出现问题的不是 IE8,而是 IE9 和 IE10。
缘由是 ocs 实例建立失败,由于没有调用父类的构造函数。
经过安装 transform-es2015-classes
和 transform-proto-to-assign
解决。
在配置项加上这两个插件的配置:
{ "plugins": [ ["transform-es2015-classes", { "loose": true }], "transform-proto-to-assign" ] }
虽然 postMessage
是 HTML5 的特性,但 IE8 和 Firefox3 很早就实现了这个 API,固然,跟后来的标准并不一致。这其实也不能怪 IE8。
The postMessage method is supported in Internet Explorer from version 8, Firefox from version 3 and Opera from version 9.5.
咱们可能会这样去使用:
parent.postMessage({success: 'ok', name: ‘mirreal’}, ‘*’);
可是为了兼容 IE8,咱们得转成字符串:
parent.postMessage(JSON.stringify({success: 'ok', name: "mirreal"}), ‘*’);
另一个须要注意的点是:在 IE8 下 window.postMessage
是同步的。
window.postMessage is syncronouse in IE 8
var syncronouse = true; window.onmessage = function () { console.log(syncronouse); // 在 IE8 下会在控制台打印 true }; window.postMessage('test', '*'); syncronouse = false;
遇到一个奇怪的问题,在刚开始遇到的时候(其实搞清楚缘由,好像也挺正常的),小蜜在 IE8 IE9 没法加载。在 IE8 那个古老浏览器的左下角,好像也是惟一会在页面提示脚本错误的浏览器,提示 script error
。
第一反应就是应该又是某个函数在 IE 下不支持,准备打开控制台看看到底哪里报错,结果却什么事都没有了,页面居然顺畅地加载出来了,这下该怎么调试好呢?
开始思考:什么东西是依赖控制台而存在的,到底会是什么呢。。。其实就是控制台自己。
缘由就是咱们在代码中添加了一些控制信息会打印在控制台,而 IE8/IE9 要开启 IE Dev Tools 才能使用 console
对象。
切忌把 IE8/9 想成 Chrome/Firefox,觉得永远有 window.console
可用.终于,IE10 改邪归正,console
再也不像段誉的六脉神剑时有时无。
console.log is there in IE8, but the console object isn't created until you open DevTools. Therefore, a call to console.log may result in an error, for example if it occurs on page load before you have a chance to open the dev tools.
但只要 IE8/9 还在一天,console 检查仍是不能少的
事实上,IE8/9 从未死去,因此
就像这样:
if (window.console) { console.log('log here'); }
要是有一堆 console.log
, console.count
, console.error
, console.time
, console.profile
,... 这样去写,那还不把人写到恶心死。
写个简单的 console polyfill 吧,检测是否存在 console
,不存在能够常见一个同名的空方法达到不报错的目的。固然,生产环境的代码其实也不会有那么多奇奇怪怪的 console
。
X-UA-Compatible
当初是针对 IE8 新加的一个配置。用于为 IE8 指定不一样的页面渲染模式,好比使用 IE7 兼容模式,或者是采用最新的引擎。
如今基本也不须要前者的降级模式,更多的是写入 IE=edge
支持最新特性。而 chrome=1
则会激活 Google Chrome Frame,前提是你的 IE 安装过这个插件。
有什么用呢,固然有用,有些 API 是做为新特性存在于 IE8 中的,好比 JSON
,不开启的话就用不了。
在 IE8 刚推出的时候,不少网页因为重构的问题,没法适应较高级的浏览器,因此使用 X-UA-Compatible
强制 IE8 采用低版本方式渲染。
好比:使用下面这段代码后,开发者无需考虑网页是否兼容 IE8 浏览器,只要确保网页在 IE六、IE7 下的表现就能够了。
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
而这段代码:
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
IE=edge
告诉 IE 使用最新的引擎渲染网页,chrome=1
则能够激活 Chrome Frame[1]。
最后说说 IE 的条件注释,用法以下:
! [if !IE] The NOT operator. This is placed immediately in front of the feature, operator, or subexpression to reverse the Boolean meaning of the expression. lt [if lt IE 5.5] The less-than operator. Returns true if the first argument is less than the second argument. lte [if lte IE 6] The less-than or equal operator. Returns true if the first argument is less than or equal to the second argument. gt [if gt IE 5] The greater-than operator. Returns true if the first argument is greater than the second argument. gte [if gte IE 7] The greater-than or equal operator. Returns true if the first argument is greater than or equal to the second argument. ( ) [if !(IE 7)] Subexpression operators. Used in conjunction with boolean operators to create more complex expressions. & [if (gt IE 5)&(lt IE 7)] The AND operator. Returns true if all subexpressions evaluate to true | [if (IE 6)|(IE 7)] The OR operator. Returns true if any of the subexpressions evaluates to true.
另一个相似的东西是在 Javascript 中的条件编译(conditional compilation)。咱们可使用这段简单的代码来作浏览器嗅探:
var isIE = /*@cc_on!@*/false
在其余浏览器中,false 前的被视为注释,而在 IE 中,/*@cc_on .... @*/
之间的部分能够被 IE 识别并做为程序执行,同时启用 IE 的条件编译。
经常使用变量以下:
* @_win32 若是在 Win32 系统上运行,则为 true。 * @_win16 若是在 Win16 系统上运行,则为 true。 * @_mac 若是在 Apple Macintosh 系统上运行,则为 true。 * @_alpha 若是在 DEC Alpha 处理器上运行,则为 true。 * @_x86 若是在 Intel 处理器上运行,则为 true。 * @_mc680x0 若是在 Motorola 680x0 处理器上运行,则为 true。 * @_PowerPC 若是在 Motorola PowerPC 处理器上运行,则为 true。 * @_jscript 始终为 true。 * @_jscript_build 包含 JavaScript 脚本引擎的生成号。 * @_jscript_version 包含 major.minor 格式的 JavaScript 版本号。
Internet Explorer 11 以前的全部版本的 Internet Explorer 都支持条件编译。 从 Internet Explorer 11 标准模式开始,Windows 8.x 应用商店应用不支持条件编译。
以前一直在作移动端的开发,没想到作 PC 端也会遇到这么多的兼容性问题。不一样于移动端设备的繁杂和不肯定性,PC 版的兼容更侧重于对特定浏览器的特性的了解,相比而言更为明确,而非由于某一款手机的诡异表现。