Vue 兼容 ie9 的全面解決方案[轉(zhuǎn)]

原文

前言

背景情況

  • vue - 2.5.11
  • vue-cli 使用模板 webpack-simple
  • http請求:axios

Vue 官方對于 ie 瀏覽器版本兼容情況的描述是 ie9+抖坪,即是 ie9 及更高的版本沛励。經(jīng)過測試眉厨,Vue 的核心框架 vuejs 本身僻他,以及生態(tài)的官方核心插件(VueRouter、Vuex等)均可以在 ie9 上正常使用环揽。

Vue 的作者尤雨溪對于 Vue 的學習建議 中有提及為了將項目更好的生態(tài)化/工程化略荡,要盡可能學習及使用新的 ECMAScript 規(guī)范。目前 ES6/ES2015 是可用度和穩(wěn)定度較高的規(guī)范歉胶,文檔齊全汛兜,國內(nèi)還有 阮一峰 《ECMAScript 6 入門》 做了大量的文檔翻譯,開發(fā)環(huán)境可謂完善通今。然而版本較舊的瀏覽器并不支持 es6 規(guī)范粥谬,尤其是 ie 瀏覽器,即使是最高的 ie11 版本辫塌,對于 es6 規(guī)范也支持得并不全漏策。如此則需要對所有原生不支持 ES6 特性的瀏覽器做兼容性處理。

本文將針對使用 Vue 生態(tài)開發(fā)完成的網(wǎng)站臼氨,以 ie9 版本為基礎(chǔ)兼容目標掺喻,實現(xiàn)全功能正常使用的全面兼容解決方案。

ES6兼容

在 ie9 的環(huán)境上一也,es6 的部分新對象巢寡、表達式,并不支持椰苟,解決方案是使用 babel-polyfill 組件抑月,它可以將 es6 的代碼翻譯成低版本瀏覽器可以識別的 es5 代碼

npm i babel-polyfill --save-dev

安裝完成后,在項目的主入口文件 main.js 的首行就可以直接引用

import 'babel-polyfill';

在項目使用 vue-cli 生成的代碼中舆蝴,根目錄有一個 .babelrc 文件谦絮,這是項目使用 babel 的配置文件题诵。在默認生成的模板內(nèi)容中,增加 "useBuiltIns": "entry" 的設(shè)置內(nèi)容层皱,這是一個指定哪些內(nèi)容需要被 polyfill(兼容) 的設(shè)置

useBuiltIns 有三個設(shè)置選項

  • false - 不做任何操作
  • entry - 根據(jù)瀏覽器版本的支持性锭,將 polyfill 需求拆分引入,僅引入有瀏覽器不支持的polyfill
  • usage - 檢測代碼中 ES6/7/8 等的使用情況叫胖,僅僅加載代碼中用到的 polyfill

這里推薦設(shè)置為 entry 草冈,完整的 .babelrc 內(nèi)容如下:

{
  "presets": [
    [
      "env",
      {
        "modules": false,
        "useBuiltIns": "entry"
      }
    ],
    "stage-3"
  ]
}

加入這些代碼后,工程里的大部分內(nèi)容已可兼容到 ie9 版本

Number對象

即使在使用 babel-polyfill 做代碼翻譯后瓮增,發(fā)現(xiàn)還是有一些 es6 的新特性并沒有解決怎棱,比如說 Number 對象的 parseIntparseFloat 方法

es6 將全局方法 parseInt()parseFloat() ,移植到 Number 對象上面绷跑,行為完全保持不變拳恋。這樣做的目的,是逐步減少全局性方法砸捏,使得語言逐步模塊化谬运。

解決這個問題不需要引入包來解決,同樣在項目主入口文件 main.js 加入以下代碼(代碼盡可能靠前垦藏,最好是在引用 babel-polyfill 之后 )

if (Number.parseInt === undefined) Number.parseInt = window.parseInt;
if (Number.parseFloat === undefined) Number.parseFloat = window.parseFloat;

requestAnimationFrame方法

window.requestAnimationFrame 是瀏覽器用于定時循環(huán)操作的一個接口梆暖,類似于 setTimeout,主要用途是按幀對網(wǎng)頁進行重繪膝藕。

requestAnimationFrame 的優(yōu)勢式廷,在于充分利用顯示器的刷新機制咐扭,比較節(jié)省系統(tǒng)資源芭挽。顯示器有固定的刷新頻率(60Hz或75Hz),也就是說蝗肪,每秒最多只能重繪60次或75次袜爪,requestAnimationFrame 的基本思想就是與這個刷新頻率保持同步,利用這個刷新頻率進行頁面重繪薛闪。此外辛馆,使用這個API,一旦頁面不處于瀏覽器的當前標簽豁延,就會自動停止刷新昙篙。這就節(jié)省了CPU、GPU和電力诱咏。

不過有一點需要注意苔可,requestAnimationFrame 是在主線程上完成。這意味著袋狞,如果主線程非常繁忙焚辅,requestAnimationFrame 的動畫效果會大打折扣映屋。

window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù)同蜻,這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用棚点。

有部分第三方組件就使用了這個方法,例如部分文件上傳湾蔓、圖片處理類的組件瘫析;那么在這類型的組件在 ie9 下使用時,會報出

SCRIPT5007: Expected object.

window.requestAnimationFrame() 的最低兼容 ie 版本為 10默责,那么在 ie9 上做兼容就需要制作 requestAnimationFrame polyfill

(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

Gist:requestAnimationFrame polyfill

Loading...

這部分代碼同樣是盡可能在網(wǎng)站入口處就執(zhí)行

http網(wǎng)絡(luò)請求(跨域)

在大多數(shù)的 Web 項目中(以 JavaWeb 為例)颁股,網(wǎng)站的頁面和服務(wù)(至少是 controller 層)在同一個工程進行開發(fā)和部署,在大前端的新型模式下傻丝,我們建議盡可能對網(wǎng)站的前端和后端進行完全分離甘有,前后端分離的好處和意義這里不再贅述。

既然是前后端分離葡缰,那么部署也必然是各自獨立部署亏掀,不同的訪問路徑,就會產(chǎn)生跨域訪問的問題(同一站點泛释,不同端口號也是跨域)

在此設(shè)定背景情況:

  • 服務(wù)端已完整開啟 CROS 跨域支持
  • http 組件使用 axios
  • axios 設(shè)置 withCredentials 為 true 開啟跨域訪問時攜帶 cookie 數(shù)據(jù)

高版本瀏覽器(ie10+ 或 chrome, ff)僅需要完成背景情況中的功能滤愕,即可支持跨域數(shù)據(jù)請求功能

axios 進行數(shù)據(jù)請求時,默認使用 XMLHttpRequest 對象怜校,在檢測到當前請求是跨域訪問時间影,axios 會測試瀏覽器是否支持 XDomainRequest 對象,若支持則優(yōu)先使用茄茁。

ie8 / ie9 的 XMLHttpRequest 對象魂贬,不支持跨域訪問,該對象在 ie10 后才原生支持跨域訪問裙顽。微軟的解決方案是在 ie8 / ie9 中提供了 XDomainRequest(XDR) 對象來進行解決跨域問題付燥,雖然使用該對象可以跨域訪問成功,并返回數(shù)據(jù)愈犹,但它卻依然是一個功能不完整的半成品键科,它的使用有諸多限制:

  • XDR 僅支持 GET 與 POST 兩種請求方式
  • XDR 不支持自定義的請求頭,若服務(wù)端使用 header 的自定義參數(shù)進行做身份驗證漩怎,則不可用
  • 請求頭的 Content-Type 只允許設(shè)置為 text/plain
  • XDR 不允許跨協(xié)議的請求勋颖,如果網(wǎng)頁在 HTTP 協(xié)議下,就只能請求 HTTP 協(xié)議下的接口勋锤,不能訪問 HTTPS 接口
  • XDR 只接受HTTP/HTTPS 的請求
  • 發(fā)起請求的時候饭玲,不會攜帶 authenticationcookies

微軟雖然提供了解決方案,但卻是不折不扣的雞肋怪得,根本無法勝任系統(tǒng)中各種場景的數(shù)據(jù)請求需求咱枉,至此卑硫,axios 對 ie9 的跨域數(shù)據(jù)請求已無能為力。

完美解決方案:代理(proxy)

雖然 axios 對 ie9 跨域已無能為力蚕断,但前端項目打包的解決方案 webpack 提供了一個優(yōu)雅而徹底解決問題的方式:代理

devServer.proxy

webpack 的 devServer.proxy 的功能是由 http-proxy-middleware 項目來實現(xiàn)的

實現(xiàn)原理是將目標位置的請求代理為前端服務(wù)本地的請求欢伏,既然是代理成為本地的請求,就不存在跨域的問題亿乳,axios 就會用回 XMLHttpRequest 對象進行數(shù)據(jù)請求硝拧,一切都恢復(fù)正常了,header葛假、cookies障陶、content-type、authentication 等內(nèi)容都被正確傳遞到服務(wù)端聊训。

項目中 webpack.config.js 的配置

devServer: {
    historyApiFallback: true,
    noInfo: true,
    overlay: true,
    proxy: {
        '/api': {
            target: 'http://localhost:8081/myserver',
            pathRewrite: {
                '^/api': ''
            }
        }
    }
}

配置中指定了將 http://localhost:8081/myserver 服務(wù)的位置代理為本地前端服務(wù)的 http://localhost:8080/api抱究。例如需要讀取用戶信息的原請求是 http://localhost:8081/myserver/user/zhangsan,代理后带斑,就變?yōu)?http://localhost:8080/api/user/zhangsan鼓寺。

即是 /api 的前綴代表了服務(wù)端,所以在使用 axios 時勋磕,需要對每個服務(wù)端請求都增加上 /api 的前綴妈候;通常在項目開發(fā)中,需要對數(shù)據(jù)請求組件 axios 進行二次封裝挂滓,以達到統(tǒng)一設(shè)置默認參數(shù)苦银,統(tǒng)一數(shù)據(jù)請求入口等目的,那么此時就只需要在二次封裝的文件里統(tǒng)一調(diào)整請求前綴即可赶站。

不過幔虏,webpack 的 devServer.proxy 僅在開發(fā)模式下可用,生產(chǎn)模式下無法使用亲怠。開發(fā)模式下所计,調(diào)試服務(wù)可以讀取 webpack.config.js 中的配置內(nèi)容進行實時代理柠辞,而項目在部署到生產(chǎn)環(huán)境前团秽,需要將工程進行編譯轉(zhuǎn)換成靜態(tài)的 js 文件,沒有調(diào)試服務(wù)的支撐自然是無法進行請求代理的叭首。

nginx 配置

雖然 devServer.proxy 的功能僅能工作于開發(fā)模式习勤,那么在生產(chǎn)模式下,自然也是有解決方案的焙格;通常 Vue 的項目在編譯成最終的 js 文件后图毕,僅需要靜態(tài)服務(wù)器即可,這其中又以 nginx 為最優(yōu)選擇方案眷唉,輕量予颤、高性能囤官、高并發(fā)、反向代理服務(wù)等均為其優(yōu)點蛤虐,這里需要做的數(shù)據(jù)請求代理的功能就使用到了 nginx 的 反向代理 功能

conf/nginx.conf 文件配置增加以下內(nèi)容

location /api/ {
    proxy_pass http://localhost:8081/myserver/;
}

該配置同樣是將 http://localhost:8081/myserver/ 的目標服務(wù)端位置代理為本地服務(wù)的 /api 路徑党饮,如此,生產(chǎn)環(huán)境下的數(shù)據(jù)請求問題也得以解決

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驳庭,一起剝皮案震驚了整個濱河市刑顺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饲常,老刑警劉巖蹲堂,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贝淤,居然都是意外死亡柒竞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門播聪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來能犯,“玉大人,你說我怎么就攤上這事犬耻〔染В” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵枕磁,是天一觀的道長渡蜻。 經(jīng)常有香客問我,道長计济,這世上最難降的妖魔是什么茸苇? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮沦寂,結(jié)果婚禮上学密,老公的妹妹穿的比我還像新娘。我一直安慰自己传藏,他們只是感情好腻暮,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毯侦,像睡著了一般哭靖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侈离,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天试幽,我揣著相機與錄音,去河邊找鬼卦碾。 笑死铺坞,一個胖子當著我的面吹牛起宽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播济榨,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼燎含,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腿短?” 一聲冷哼從身側(cè)響起屏箍,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎橘忱,沒想到半個月后赴魁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡钝诚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年颖御,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凝颇。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡潘拱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拧略,到底是詐尸還是另有隱情芦岂,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布垫蛆,位于F島的核電站禽最,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袱饭。R本人自食惡果不足惜川无,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虑乖。 院中可真熱鬧懦趋,春花似錦、人聲如沸疹味。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佛猛。三九已至惑芭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間继找,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工逃沿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留婴渡,地道東北人幻锁。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像边臼,于是被迫代替她去往敵國和親哄尔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

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