之前對瀏覽器兼容性問題只是大概知道一些點,沒想到此次真正着手去作的時候,仍是碰到了不少問題。剛開始的時候一邊解決問題,一邊想着:用 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 安裝:sql
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 版的兼容更側重於對特定瀏覽器的特性的瞭解,相比而言更爲明確,而非由於某一款手機的詭異表現。
使用 F12 工具控制檯查看錯誤和狀態.aspx)
定義文檔兼容性.aspx)
條件編譯 (JavaScript).aspx)