知識 分享 互助 懶人建站

    懶人建站專注于網頁素材下載,提供網站模板、網頁設計、ps素材、圖片素材等,服務于【個人站長】【網頁設計師】和【web開發從業者】的代碼素材與設計素材網站。

    懶人建站提供網頁素材下載、網站模板
    知識 分享 互助!

    reactjs+webpack的ie8兼容問題解決方案

    作者:佳明媽 來源:aliued 2017-11-16 人氣:
    reactjs+webpack的ie8兼容問題解決方案,webpack 進行babel對ES6,7語法的轉碼。 webpack 引用es3ify-loader 解決es3語法兼容問題。 全局引用babel-polyfill,es5-shim/es5-sham,console-polyfill,JSON的polyfill等 不要

        reactjs+webpack的ie8兼容問題解決方案,原文來自 aliued,不過這篇文章原文基本是不能訪問了,你可以試試還能不能訪問:www.aliued.com/?p=3240

        首先說一下兼容ie8的通用方案,網上搜到一些方法,比如添加 transform-es3-property-literals,transform-es3-member-expression-literal , add-module-exports 插件等,不過它們可能是不同時期的不同解決方案,實際上是在解決同一類問題,即es3環境對es5語法的兼容。

        一、es3ify解決es3環境兼容

        對于這個問題,主要是解決es3的保留字在es3環境下的正確使用,default是暴露最多的問題,因為大家都在寫export default xx。對于這個問題,目前比較快捷的方式就是使用es3ify,在webpack中就是添加es3ify-loader,代碼如下:

    module: {
                loaders: [{
                    test: /.jsx?$/,
                    loaders: ['es3ify-loader'],
                }
            ]
        }

        它主要做的事情就是對于一些保留字的使用做了es3兼容,以及一些額外的處理,比如去除數組尾部的多余逗號:

    // babel轉換前
    var a = {
        class: "haha"
    }
    a.class = "bb";
    var arr = [1,2,3,];
    
    //babel轉換后
    var a = {
        "class": "haha"
    }
    a["class"] = "bb";
    var arr = [1,2,3];

        有了它,其它的一些插件transform-es3-property-literals,transform-es3-member-expression-literal,add-module-exports 就沒有必要了,你會發現這些插件就是在解決部分問題:

        transform-es3-property-literals:保證在對象屬性中的保留字正確

    // babel轉換前
    var a = {
        class: "haha"  //變動處
    }
    a.class = "bb";
    
    //babel轉換后
    var a = {
        "class": "haha"
    }
    a.class = "bb";    //變動處

        transform-es3-member-expression-literal:保證在對象屬性訪問中的保留字正確

    // babel轉換前
    var a = {
        class: "haha"   
    }
    a.class = "bb";   //變動處
    
    //babel轉換后
    var a = {
        class: "haha"
    }
    a["class"] = "bb";//變動處

        所以也會把export.default轉為export[“default”], 即解決了default不兼容問題。

        add-module-exports:僅僅解決default的問題

        二、babel-polyfill 解決缺失API問題

        先跨過Object.defineProperty問題,因為那里是重要的坑點。而對于上面的這三個問題,實際上屬于同一類問題,即對一些ES6 API缺失的模擬。比如常見的Object.assign,Promise對象,fetch等等,這些可以通過統一引用“babel-polyfill”來解決,如果感覺“babel-polyfill”太重,也可以針對所需要的API自行引用對應的polyfill。polyfill的應用可以有兩種方式:

    1.             npm包的方式,在編譯入口文件通過require(“babel-polyfill”)引入執行。

    2.             也可以在頁面上,業務js前引入babel的script標簽。

            這里最后一個問題:

            以及console對象的兼容問題就比較簡單,也都可以通過對應polyfill解決,就不多做解釋。

        三、最麻煩的Object.defineProperty

        這里整個問題的說明已經滯后,且有錯誤:

        首先,這里說的 “Object.defineProperty 在IE8中不存在” 是錯的,而是IE8中有自己實現的Object.defineProperty,它的行為和標準不同,且只能接受DOM對象,如果傳入普通javascript對象會拋異常。詳細說明在這里 Object.defineProperty 。

        其次,babel會把 export(非import) 編譯成 Object.defineProperty的方式。相信添加這個問題的時候,babel確實存在這樣的轉換,具體的issues也有人提過 babel-export,而提供的解決方案—-引入es5-shim和es5-sham在這種情況下是也確實是可行的。不過目前的babel版本已經不會有這種轉換(卻還存另一個轉換的坑),但是es5-shim和es5-sham的引用是必要的,因為它是解決通用性的es3環境下es5 API的缺失問題,就像babel-polyfill一樣,Object.defineProperty是其中的一個API。

        以上是現有常規的兼容方案,很多人使用也沒有存在太多問題。

        遇到的問題和排查

        本以為按照這樣的指引,進行了babel轉換,引入es3ify,babel-polyfill,es5-shim/es5-sham,console-polifill就大功告成了,可惜ie8運行起來還是崩了。先是錯誤定位到babel-polyfill中,去掉babel-polyfill又定位到es5-sham中,報的錯誤都是異常未捕獲,且在IE8下調試很艱難。后來根據es5-sham壓縮代碼的拋異常位置,查看es5-sham的源碼,結合es5-sham的文檔說明,基本定位為Object.defineProperty的問題。

            首先其拋異常的代碼在這段:

        代碼會在supportsAccessors為false且hasGetter或者hasSetter時拋異常,邏輯上講就是如果當前js引擎不支持訪問器屬性,但是卻在屬性描述符中設置了get,set,那么就會拋出異常。supportsAccessors用于判斷當前js引擎是否支持訪問器屬性,它的判斷邏輯在這里:

            實際就是用Object.prototype.hasOwnProperty(“ defineGetter “)做判斷,“ defineGetter ”的兼容情況是只兼容IE11,具體查看 defineGetter 說明 ,雖然ie9,ie10同樣不支持 defineGetter ,不過他們直接支持 Object.defineProperty 方法和 get語法 ,無需sham,所以代碼并不會走到異常這里。實際上es5-sham官方文檔也提到對Object.defineProperty的polyfill會存在限制和fail的情況:

            具體查看: es5-shim , 雖然其說明和代碼實現存在些差異,不過結論是明確的:ie8下訪問器屬性不支持,會拋異常;

            基本明確了是用Object.defineProperty()設置訪問器屬性的問題,那么就向上查找到底是哪里使用了訪問器屬性設置,在編譯后的源代碼里搜索“ :get”查到了這段代碼:

            根據上面關鍵字syncHistoryWithStore,routerReducer等初步判斷是在react-router-redux中,node_modules查看react-router-redux源碼,果然,其lib中index.js里有很多對export的訪問器屬性設置。再查看對應src/index.js文件,兩份代碼如下:

    //src/index.js
    export syncHistoryWithStore from './sync'
    export { LOCATION_CHANGE, routerReducer } from './reducer'
    
    export {
        CALL_HISTORY_METHOD,
        push, replace, go, goBack, goForward,
        routerActions
    } from './actions'
    export routerMiddleware from './middleware'

        babel編譯后部分代碼:

    //lib/index.js
    'use strict';
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.routerMiddleware = exports.routerActions = exports.goForward = exports.goBack = exports.go = exports.replace = exports.push = exports.CALL_HISTORY_METHOD = exports.routerReducer = exports.LOCATION_CHANGE = exports.syncHistoryWithStore = undefined;
    
    var _reducer = require('./reducer');
    
    Object.defineProperty(exports, 'LOCATION_CHANGE', {
      enumerable: true,
      get: function get() {
        return _reducer.LOCATION_CHANGE;
      }
    });
    Object.defineProperty(exports, 'routerReducer', {
      enumerable: true,
      get: function get() {
        return _reducer.routerReducer;
      }
    });

        所以確定了是babel編譯問題,又回到了上面提到的,babel對直接的export會轉碼為Object.defineProperty,但是它們修復了這個問題,不過好死不死的,對于export 和 import結合的寫法—-export xx from ‘xxx’ 還是會轉碼為Object.defineProperty對exports進行屬性設置,更加嚴重的是,之前的Object.defineProperty設置的是數據屬性,直接指定的value,但是這次的Object.defineProperty 腦殘般的設置了訪問器屬性,如此一來es5-sham已經沒有辦法解決(上面已經說明)。

        所以最終的排查結果就是主要3點:

    1.             ie8不支持設置訪問器屬性,即便是引了es5-shim;

    2.             Babel 會把export xxx from ‘xx’ 語法轉碼為訪問器屬性設置的exports對象。

    3.             而react-router-react的index.js 偏偏用了export xxx from ‘xx’這樣的語法。

        解決方案

        最直接的解決辦法就是Babel修復這種轉碼方式,目前已經有人提過issue,但是尚未解決: issue

        其次react-router-redux 不要用export xx from ‘xx’的方式,也有人提過issue: issue

        不過目前為了解決線上bug,肯定沒有辦法等待它們的修復,且尚未找到一種ie8下兼容訪問器屬性設置的方法,所以最終此項目的解決方案是修改react-router-react的源碼,把其中的export xx from ‘xx’的語法改成分開的方式,比如:

    //修改前
    export syncHistoryWithStore from './sync'
    
    //修改后
    import syncHistoryWithStore from './sync';
    export {syncHistoryWithStore} ;

        然后所有引用react-router-react的地方改為對src/index.js的引用,在項目中自己重新編譯,而不使用lib中編譯好的版本。

        總結

        原本對webpack和babel了解的就不是很多,差不多用了一天的時間來排查這個問題,感覺react的項目想要支持ie8坑還會很多,其中包括babel,webpack,第三方庫對ie8的兼容支持問題并不良好,且現在 [email protected] 版本已經放棄ie8。所以目前實踐的ie8兼容方案是:

    1.             webpack 進行babel對ES6,7語法的轉碼。

    2.             webpack 引用es3ify-loader 解決es3語法兼容問題。

    3.             全局引用babel-polyfill,es5-shim/es5-sham,console-polyfill,JSON的polyfill等

    4.             不要在代碼中用Object.defineProperty設置訪問器屬性,若第三方包中有,找到,改之。

        各位還遇到哪些問題可以一起討論,積累經驗,整個排查過程也對很多知識理解的深刻了一點。

    ↓ 查看全文

    reactjs+webpack的ie8兼容問題解決方案由懶人建站收集整理,您可以自由傳播,請主動帶上本文鏈接

    懶人建站就是免費分享,覺得有用就多來支持一下,沒有能幫到您,懶人也只能表示遺憾,希望有一天能幫到您。

    reactjs+webpack的ie8兼容問題解決方案-最新評論

    老子是皇帝在线客服