據說你的 fetch 還要兼容 IE9

Chrome:  You will die!
IE9:     Not today!
複製代碼

背景

搭建公司官網的框架時採用了 vuejs, 使用 history router mode 來作 SEO 優化, 使用 fetch 作網絡請求, fetch 用 whatwg-fetch 作 polyfill. 根據百度瀏覽器市場份額統計, 2017年整年 IE9 的佔有率達到 9.50%, 而且 vue 框架也是兼容到 IE9, 因此要求項目兼容到 IE9.vue

可是 fetch polyfill 並不兼容 IE9, 這篇文章追溯問題緣由並提出解決方法.git

問題: 訪問拒絕

在 IE9 下打開頁面, 發現 fetch 請求報了Unhandled promise rejectionError: 拒絕訪問:github

  • IE9 ajax

    訪問拒絕

  • IE11 開 IE9 調試模式 shell

    訪問拒絕

懷疑是 fetch 的兼容問題, 查看一下版本:npm

$npm list whatwg-fetch 
project
└── whatwg-fetch@2.0.3 
複製代碼

查看了一下whatwg-fetch 兼容性: 只支持到 IE10. 而後看到 whatwg-fetchv0.11 能夠兼容 IE9, 那就降級一下吧:json

$ npm uninstall whatwg-fetch
removed 1 package in 4.851s
 $ npm install whatwg-fetch@0.11
+ whatwg-fetch@0.11.1
added 1 package in 5.96s
複製代碼

再試一下, 發現仍是同樣的問題.跨域

問題緣由: IE9 XMLHttpRequest 不支持 CORS

fetch 的 polyfill 採用了 XMLHttpRequest 實現, 可是在 IE9 下面, XMLHttpRequest 是不支持跨域請求的. IE10 的 XMLHttpRequest 支持跨域, 而 IE8, IE9 須要使用 XDomainRequest 來實現跨域.promise

那就用 XDomainRequest 實現異步請求, 代碼:瀏覽器

function fetchIe9(url, options = {}) => {
  if (window.XDomainRequest) {
    // https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest
    // only support GET and POST method
    // request and response content type should be JSON
    // without response status code
    return new Promise((resolve, reject) => {
      const method = options.method || 'GET';
      const timeout = options.timeout || 30000;
      let data = options.body || options.params || {};
      if (data instanceof Object) {
        data = JSON.stringify(data);
      }

      const XDR = new XDomainRequest();
      XDR.open(method, url);
      XDR.timeout = timeout;
      XDR.onload = () => {
        try {
          const json = JSON.parse(XDR.responseText);
          return resolve(json.data);
        } catch (e) {
          reject(e);
        }
        return reject({});
      };
      XDR.ontimeout = () => reject('XDomainRequest timeout');
      XDR.onerror = () => reject('XDomainRequest error');
      XDR.send(data);
    });
  } else {
    // native fetch or polyfill fetch(XMLHttpRequest)
    // fetch...
  }
}
複製代碼

須要注意的是:

  • XDomainRequest 只支持 GET 和 POST mehtod
  • XDomainRequest 不支持帶 cookie
  • XDomainRequest 不能設置 responseType, 通訊雙方須要約定數據格式
  • XDomainRequest 的響應沒有 response status code

題外話: whatwg-fetch 一直採用 XMLHttpRequest 來作 polyfill, whatwg-fetch1.0+ 不支持 IE9, 並非由於沒有采用 XDomainRequest, 而是由於 IE9 的狀態碼不符合 fetch 規範, 而 polyfill 的目標是 polyfill 規範, 而不是作兼容.

問題: 請求異常終止和掛起

寫好了代碼, 在 IE9 中, 網絡請求很是詭異, 常常不行: 請求只持續了不到 1ms, 而且接收數據爲 0B, 沒有狀態碼; 可是在少數時候是能夠成功請求並獲取數據的.

  • IE9

    異常終止

  • IE11 開 E9 調試模式 此時 IE11 的 IE9 調試模式是能夠的, 看來模擬器仍是模擬不到位.

    沒有異常終止

查了很久, 終於看到一篇文章: Internet Explorer Aborting AJAX Requests : FIXED

IE timing out the request even though data is being transmitted.

主要的緣由大概是 IE9 會將一個正在傳輸的請求 timeout 掉.

解決辦法是:

  • 添加 onprogress 事件回調, 告知 IE9 這個請求是活動中的, 不要 timeout 掉.
  • 將請求的發送放到主線程以外, 保證 XDomainRequest 已經徹底初始化好.

最終代碼

function fetchIe9(url, options = {}) => {
  if (window.XDomainRequest) {
    // https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest
    // only support GET and POST method
    // request and response content type should be JSON
    // without response status code
    return new Promise((resolve, reject) => {
      const method = options.method || 'GET';
      const timeout = options.timeout || 30000;
      let data = options.body || options.params || {};
      if (data instanceof Object) {
        data = JSON.stringify(data);
      }

      const XDR = new XDomainRequest();
      XDR.open(method, url);
      XDR.timeout = timeout;
      XDR.onload = () => {
        try {
          const json = JSON.parse(XDR.responseText);
          return resolve(json.data);
        } catch (e) {
          reject(e);
        }
        return reject({});
      };
      // fix random aborting: https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/
      XDR.onprogress = () => {};
      XDR.ontimeout = () => reject('XDomainRequest timeout');
      XDR.onerror = () => reject('XDomainRequest error');
      setTimeout(() => {
        XDR.send(data);
      }, 0);
    });
  } else {
    // native fetch or polyfill fetch(XMLHttpRequest)
    // fetch...
  }
}
複製代碼

結論

  • IE9 發起跨域請求要使用 XDomainRequest, 由於 IE9 下的 XMLHttpRequest 不支持跨域調用.
  • XDomainRequest 只支持 GET 和 POST method, 而且沒有 response status code, 能夠說是不完善的 HTTP 異步請求對象.
  • XDomainRequest 不支持指定 responseType, 使用時建議請求和返回數據格式約定爲 JSON.
  • whatwg-fetch1.0+ 不支持 IE9, 是由於 IE9 的狀態碼不符合 fetch 規範, 而 polyfill 的目標是 polyfill 規範, 而不是作兼容.

References

  • XDomainRequest

https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest

  • XMLHttpRequest

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

  • XDomainRequest – Restrictions, Limitations and Workarounds

https://blogs.msdn.microsoft.com/ieinternals/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds/

  • Internet Explorer Aborting AJAX Requests : FIXED

https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/

EOF

相關文章
相關標籤/搜索