Chrome: You will die!
IE9: Not today!
複製程式碼
背景
搭建公司官網的框架時採用了 vuejs, 使用 history router mode 來做 SEO 優化, 使用 fetch 做網路請求, fetch 用 whatwg-fetch 做 polyfill. 根據百度瀏覽器市場份額統計, 2017年全年 IE9 的佔有率達到 9.50%, 並且 vue 框架也是相容到 IE9, 所以要求專案相容到 IE9.
但是 fetch polyfill 並不相容 IE9, 這篇文章追溯問題原因並提出解決方法.
問題: 訪問拒絕
在 IE9 下開啟頁面, 發現 fetch 請求報了Unhandled promise rejectionError: 拒絕訪問
:
-
IE9
-
IE11 開 IE9 除錯模式
懷疑是 fetch 的相容問題, 檢視一下版本:
$npm list whatwg-fetch
project
└── whatwg-fetch@2.0.3
複製程式碼
檢視了一下whatwg-fetch 相容性: 只支援到 IE10. 然後看到 whatwg-fetchv0.11 可以相容 IE9, 那就降級一下吧:
$ 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 來實現跨域.
那就用 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 mehtodXDomainRequest
不支援帶 cookieXDomainRequest
不能設定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/