使用 ES6 的瀏覽器兼容性問題

以前對瀏覽器兼容性問題只是大概知道一些點(diǎn)透绩,沒想到這次真正著手去做的時候寻馏,還是碰到了很多問題棋弥。剛開始的時候一邊解決問題,一邊想著:用 IE8 的都是神經(jīng)病诚欠,到后來顽染,我發(fā)現(xiàn)完了,I LOVE IE轰绵。

0x00 起源

在這次做小蜜 PC 版的時候粉寞,由于早于 PC 版,無線版已經(jīng)重新設(shè)計(jì)了全新版左腔,做了很多架構(gòu)上的優(yōu)化調(diào)整唧垦。所以在做的時候把無線版的前端架構(gòu)拿了過來,主要的考慮就是品牌和功能保持跟無線版統(tǒng)一的同時液样,技術(shù)上也可相互支持以及組件復(fù)用振亮。

無線版整個架構(gòu)設(shè)計(jì)是同事做的,技術(shù)上主要采用 ES6 + Webpack + Babel 的方式鞭莽,由于項(xiàng)目的獨(dú)特性和特殊需求双炕,并沒有使用任何框架,只引入 zepto 作為一個標(biāo)準(zhǔn)支撐庫撮抓。

而 PC 版的架構(gòu)跟無線版基本保持一致妇斤,主要是把 zepto 換成了 jQuery。

下面是一些基本的開發(fā)依賴:

{
  "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"
  }
}

0x01 polyfill

由于 Babel 默認(rèn)只轉(zhuǎn)換轉(zhuǎn)各種 ES2015 語法,而不轉(zhuǎn)換新的 API站超,比如 Promise荸恕,以及 Object.assign、Array.from 這些新方法死相,這時我們需要提供一些 ployfill 來模擬出這樣一個提供原生支持功能的瀏覽器環(huán)境融求。

主要有兩種方式:babel-runtimebabel-polyfill

babel-runtime

babel-runtime 的作用是模擬 ES2015 環(huán)境,包含各種分散的 polyfill 模塊,我們可以在自己的模塊里單獨(dú)引入厢破,比如 promise:

import 'babel-runtime/core-js/promise'

它們不會在全局環(huán)境添加未實(shí)現(xiàn)的方法吧雹,只是這樣手動引用每個 polyfill 會非常低效笆包,我們可以借助 Runtime transform 插件來自動化處理這一切。

首先使用 npm 安裝:

npm install babel-plugin-transform-runtime --save-dev

然后在 webpack 配置文件的 babel-loader 增加選項(xiàng):

loader: ["babel-loader"],
query: {
  plugins: [
    "transform-runtime"
  ],
  presets: ['es2015', 'stage-0']
}

babel-polyfill

babel-polyfill 是針對全局環(huán)境的,引入它瀏覽器就好像具備了規(guī)范里定義的完整的特性,一旦引入莱睁,就會跑一個 babel-polyfill 實(shí)例。用法如下:

1.安裝 babel-polyfill

npm install babel-polyfill --save

2.在入口文件中引用:

import 'babel-polyfill'

小結(jié):

其實(shí)做到這些芒澜,在大部分瀏覽器就可以正常跑了仰剿,但我們做的是一個用戶環(huán)境很不確定的產(chǎn)品,對一些年代久遠(yuǎn)但又不容忽視的運(yùn)行環(huán)境痴晦,比如 IE8南吮,我們做的還不夠。

接下來將開始講述我們在兼容性方面遇到的一些問題誊酌,和解決方法部凑。

0x02 開始在 IE8 運(yùn)行

最開始做的時候并沒有針對 IE 做一些兼容性方面的處理,結(jié)果在 IE8 上一跑一堆問題术辐。

第一步,我們把 jQuery 換成 1.12.1 施无,因?yàn)?2.X 已經(jīng)不再支持 IE8辉词。

但并沒有像我們想象中的那樣,只是簡單換一下 jQuery 版本就可以正常運(yùn)行了猾骡。

0x03 default or catch

這是遇到的第一個問題瑞躺。在兼容性測試過程中,對下面的代碼:

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

或者這種:

module.exports = _main2.default;

在 IE8 下會直接報”缺少標(biāo)識符兴想、字符串或數(shù)字”的錯幢哨。

我們得在對象的屬性上加 '' 才可以。就像下面這樣:

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { ‘default’: obj };
}

module.exports = _main2['default'];

至于原因嫂便,并不是 IE8 下對象的屬性必須得加 '' 才行捞镰,而是 default 的問題,作為一個關(guān)鍵字,同樣的問題還包括 catch岸售。

這兩種情況践樱,可以通過使用 transform-es3-property-literalstransform-es3-member-expression-literals 這兩個插件搞定。

總之凸丸,在平時寫代碼的時候避免使用關(guān)鍵字拷邢,或者保留字作為對象的屬性值,尤其是在習(xí)慣不加引號的情況下屎慢。相關(guān)討論:Allow reserved words for properties

0x04 es5-shim瞭稼、es5-sham

為了兼容像 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.

其實(shí) es5-shim 明確說明环肘,這個方法的 polyfill 在 IE8 會失敗,因?yàn)?IE8 已經(jīng)有個同名的方法妖枚,但只是用于 DOM 對象廷臼。

同樣的問題還包括 Object.create,上述問題可以再引入 es5-sham 解決.

0x05 addEventListener

項(xiàng)目中有部分代碼直接使用 addEventListener 這個 API绝页,但在 IE8 下的事件綁定并不是這個方法荠商。

這個問題很容易解決,也無需去寫額外的 polyfill续誉。我們已經(jīng)把 jQuery 換成 1.x莱没,所以只需把代碼中 addEventListener 換成 jQuery 的寫法就 Okay 了。

jQuery 其實(shí)為我們封裝了很多 API酷鸦,并做了很多兼容性的封裝饰躲,類似的只要使用封裝好的就可以了。

0x06 無法獲取未定義或 null 引用的屬性

這個問題是在特定場景下【轉(zhuǎn)人工】出現(xiàn)的臼隔,出現(xiàn)問題的不是 IE8嘹裂,而是 IE9 和 IE10。

原因是 ocs 實(shí)例創(chuàng)建失敗摔握,因?yàn)闆]有調(diào)用父類的構(gòu)造函數(shù)寄狼。

通過安裝 transform-es2015-classestransform-proto-to-assign 解決。

在配置項(xiàng)加上這兩個插件的配置:

{
  "plugins": [
      ["transform-es2015-classes", { "loose": true }],
      "transform-proto-to-assign"

  ]
}

0x07 postMessage

雖然 postMessage 是 HTML5 的特性氨淌,但 IE8 和 Firefox3 很早就實(shí)現(xiàn)了這個 API泊愧,當(dāng)然,跟后來的標(biāo)準(zhǔn)并不一致盛正。這其實(shí)也不能怪 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,我們得轉(zhuǎn)成字符串:

parent.postMessage(JSON.stringify({success: 'ok', name: "mirreal"}), ‘*’);

另外一個需要注意的點(diǎn)是:在 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;

0x08 IE8/IE9 的控制臺

遇到一個奇怪的問題痰滋,在剛開始遇到的時候(其實(shí)搞清楚原因摘能,好像也挺正常的),小蜜在 IE8 IE9 無法加載即寡。在 IE8 那個古老瀏覽器的左下角徊哑,好像也是唯一會在頁面提示腳本錯誤的瀏覽器,提示 script error聪富。

第一反應(yīng)就是應(yīng)該又是某個函數(shù)在 IE 下不支持莺丑,準(zhǔn)備打開控制臺看看到底哪里報錯,結(jié)果卻什么事都沒有了墩蔓,頁面竟然順暢地加載出來了梢莽,這下該怎么調(diào)試好呢?

開始思考:什么東西是依賴控制臺而存在的奸披,到底會是什么呢昏名。。阵面。其實(shí)就是控制臺本身轻局。

原因就是我們在代碼中添加了一些控制信息會打印在控制臺,而 IE8/IE9 要開啟 IE Dev Tools 才能使用 console 對象样刷。

切忌把 IE8/9 想成 Chrome/Firefox仑扑,以為永遠(yuǎn)有 window.console 可用.終于,IE10 改邪歸正置鼻,console 不再像段譽(yù)的六脈神劍時有時無镇饮。

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 檢查還是不能少的

事實(shí)上箕母,IE8/9 從未死去储藐,所以

就像這樣:


if (window.console) {
  console.log('log here');
}

要是有一堆 console.log, console.count, console.error, console.time, console.profile,... 這樣去寫嘶是,那還不把人寫到惡心死钙勃。

寫個簡單的 console polyfill 吧,檢測是否存在 console聂喇,不存在可以常見一個同名的空方法達(dá)到不報錯的目的辖源。當(dāng)然,生產(chǎn)環(huán)境的代碼其實(shí)也不會有那么多奇奇怪怪的 console授帕。

0x09 定義文檔兼容性

X-UA-Compatible 當(dāng)初是針對 IE8 新加的一個配置同木。用于為 IE8 指定不同的頁面渲染模式浮梢,比如使用 IE7 兼容模式跛十,或者是采用最新的引擎。

現(xiàn)在基本也不需要前者的降級模式秕硝,更多的是寫入 IE=edge 支持最新特性芥映。而 chrome=1 則會激活 Google Chrome Frame,前提是你的 IE 安裝過這個插件。

有什么用呢奈偏,當(dāng)然有用坞嘀,有些 API 是作為新特性存在于 IE8 中的,比如 JSON惊来,不開啟的話就用不了丽涩。

為什么要用 X-UA-Compatible?

在 IE8 剛推出的時候裁蚁,很多網(wǎng)頁由于重構(gòu)的問題矢渊,無法適應(yīng)較高級的瀏覽器,所以使用 X-UA-Compatible 強(qiáng)制 IE8 采用低版本方式渲染枉证。

比如:使用下面這段代碼后矮男,開發(fā)者無需考慮網(wǎng)頁是否兼容 IE8 瀏覽器,只要確保網(wǎng)頁在 IE6室谚、IE7 下的表現(xiàn)就可以了毡鉴。

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

而這段代碼:

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

IE=edge 告訴 IE 使用最新的引擎渲染網(wǎng)頁,chrome=1 則可以激活 Chrome Frame[1]秒赤。

0x0a 條件注釋 or 條件編譯

最后說說 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 識別并作為程序執(zhí)行崎弃,同時啟用 IE 的條件編譯甘晤。

常用變量如下:

* @_win32 如果在 Win32 系統(tǒng)上運(yùn)行,則為 true饲做。
* @_win16 如果在 Win16 系統(tǒng)上運(yùn)行线婚,則為 true。
* @_mac 如果在 Apple Macintosh 系統(tǒng)上運(yùn)行盆均,則為 true塞弊。
* @_alpha 如果在 DEC Alpha 處理器上運(yùn)行,則為 true泪姨。
* @_x86 如果在 Intel 處理器上運(yùn)行游沿,則為 true。
* @_mc680x0 如果在 Motorola 680x0 處理器上運(yùn)行肮砾,則為 true诀黍。
* @_PowerPC 如果在 Motorola PowerPC 處理器上運(yùn)行,則為 true仗处。
* @_jscript 始終為 true眯勾。
* @_jscript_build 包含 JavaScript 腳本引擎的生成號枣宫。
* @_jscript_version 包含 major.minor 格式的 JavaScript 版本號。

Internet Explorer 11 之前的所有版本的 Internet Explorer 都支持條件編譯吃环。 從 Internet Explorer 11 標(biāo)準(zhǔn)模式開始也颤,Windows 8.x 應(yīng)用商店應(yīng)用不支持條件編譯。

后:

之前一直在做移動端的開發(fā)郁轻,沒想到做 PC 端也會遇到這么多的兼容性問題翅娶。不同于移動端設(shè)備的繁雜和不確定性,PC 版的兼容更側(cè)重于對特定瀏覽器的特性的了解好唯,相比而言更為明確故觅,而非因?yàn)槟骋豢钍謾C(jī)的詭異表現(xiàn)。

參考文檔:

Allow reserved words for properties

IE8 defineProperty/getOwnPropertyDescriptor clash with shim

Runtime transform

babel-plugin-transform-runtime definitions

super() not calling parent's constructor on IE9

postMessage method (window) Javascript

使用 F12 工具控制臺查看錯誤和狀態(tài)

定義文檔兼容性

條件編譯 (JavaScript)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渠啊,一起剝皮案震驚了整個濱河市输吏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌替蛉,老刑警劉巖贯溅,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躲查,居然都是意外死亡它浅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門镣煮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姐霍,“玉大人,你說我怎么就攤上這事典唇∧髡郏” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵介衔,是天一觀的道長恨胚。 經(jīng)常有香客問我,道長炎咖,這世上最難降的妖魔是什么赃泡? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮乘盼,結(jié)果婚禮上升熊,老公的妹妹穿的比我還像新娘。我一直安慰自己绸栅,他們只是感情好级野,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阴幌,像睡著了一般勺阐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矛双,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天渊抽,我揣著相機(jī)與錄音,去河邊找鬼议忽。 笑死懒闷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的栈幸。 我是一名探鬼主播愤估,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼速址!你這毒婦竟也來了玩焰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤芍锚,失蹤者是張志新(化名)和其女友劉穎昔园,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并炮,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡默刚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逃魄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荤西。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伍俘,靈堂內(nèi)的尸體忽然破棺而出邪锌,到底是詐尸還是另有隱情,我是刑警寧澤癌瘾,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布秃流,位于F島的核電站,受9級特大地震影響柳弄,放射性物質(zhì)發(fā)生泄漏舶胀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一碧注、第九天 我趴在偏房一處隱蔽的房頂上張望嚣伐。 院中可真熱鬧,春花似錦萍丐、人聲如沸轩端。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽基茵。三九已至奋构,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拱层,已是汗流浹背弥臼。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留根灯,地道東北人径缅。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像烙肺,于是被迫代替她去往敵國和親纳猪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容