深入了解XMLHttpRequest兼容性
- Published on
- 发布于·预估阅读6分钟
- Authors
- Name
- willson-wang
XMLHttpRequest ie9+
XMLHttpRequest ie9中不支持CORS
XDomainRequest 兼容ie9中XMLHttpRequest不支持CORS的场景;但是XDomainRequest也有以下限制
- 只支持 GET 和 POST mehtod
- XDomainRequest 不支持带 cookie
- XDomainRequest 不能设置 responseType, 通信双方需要约定数据格式
- XDomainRequest 的响应没有 response status code
所以现在如果场景不需要支持到ie9,那么使用XMLHttpRequest完全足够;像axios最新版本目前兼容性就是从ie10+起
如果要兼容到ie9,且有跨域那么就需要借助XDomainRequest来实现了;
另外fetch的polyfill就是通过XMLHttpRequest对象来实现的;
whatwg-fetch目前作为fetch的常用polyfill库,最新版本兼容性ie10+起
axios源码
然后我们看下axios兼容ie9的版本,如0.18.0,只保留关键代码
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
var request = new XMLHttpRequest();
var loadEvent = 'onreadystatechange';
var xDomain = false;
// For IE 8/9 CORS support
// Only supports POST and GET calls and doesn't returns the response headers.
// DON'T do this for testing b/c XMLHttpRequest is mocked, not XDomainRequest.
if (process.env.NODE_ENV !== 'test' &&
typeof window !== 'undefined' &&
window.XDomainRequest && !('withCredentials' in request) &&
!isURLSameOrigin(config.url)) {
// 借助XDomainRequest对象来实现跨域GET|POST请求
request = new window.XDomainRequest();
loadEvent = 'onload'; // 监听的load
xDomain = true;
// 需要定义onprogress、ontimeout监听函数,避免ie下过快的终止请求
request.onprogress = function handleProgress() {};
request.ontimeout = function handleTimeout() {};
}
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
// Set the request timeout in MS
request.timeout = config.timeout;
// Listen for ready state
request[loadEvent] = function handleLoad() {
if (!request || (request.readyState !== 4 && !xDomain)) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// Prepare the response
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
// IE sends 1223 instead of 204 (https://github.com/axios/axios/issues/201)
status: request.status === 1223 ? 204 : request.status,
statusText: request.status === 1223 ? 'No Content' : request.statusText,
headers: responseHeaders,
config: config,
request: request
};
settle(resolve, reject, response);
// Clean up request
request = null;
};
// Handle low level network errors
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
// Clean up request
request = null;
};
// Handle timeout
request.ontimeout = function handleTimeout() {
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
request));
// Clean up request
request = null;
};
// Add withCredentials to request if needed
if (config.withCredentials) {
request.withCredentials = true;
}
// Add responseType to request if needed
if (config.responseType) {
try {
request.responseType = config.responseType;
} catch (e) {
// Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
// But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
if (config.responseType !== 'json') {
throw e;
}
}
}
if (requestData === undefined) {
requestData = null;
}
// Send the request
request.send(requestData);
});
};
wathwg-fetch源码
在看下wathwg-fetch的0.11.1源码,只保留关键代码
self.fetch = function(input, init) {
return new Promise(function(resolve, reject) {
var request
// 都没有去使用 XDomainRequest进行跨域请求兼容
var xhr = new XMLHttpRequest()
xhr.onload = function() {
var status = (xhr.status === 1223) ? 204 : xhr.status
if (status < 100 || status > 599) {
reject(new TypeError('Network request failed'))
return
}
var options = {
status: status,
statusText: xhr.statusText,
headers: headers(xhr),
url: responseURL()
}
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
xhr.onerror = function() {
reject(new TypeError('Network request failed'))
}
xhr.ontimeout = function() {
reject(new TypeError('Network request failed'))
}
xhr.open(request.method, request.url, true)
if (request.credentials === 'include') {
xhr.withCredentials = true
}
if ('responseType' in xhr && support.blob) {
xhr.responseType = 'blob'
}
request.headers.forEach(function(value, name) {
xhr.setRequestHeader(name, value)
})
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
})
}
总结
XMLHttpRequest的兼容性ie9+,但是ie9下不支持CORS
axios兼容ie9与不兼容ie9的区别就在是否通过XDomainRequest来支持ie9中的CORS
wathwg-fetch是fetch的polyfill库,源码内也是通过XMLHttpRequest来进行模拟
XDomainRequest为了确保安全构建,采用了多种方法。安全协议源必须匹配请求的URL。(http到http,https到https)。如果不匹配,请求会报“拒绝访问”的错误。被请求的URL的服务器必须带有 设置为(“*”)或包含了请求方的Access-Control-Allow-Origin的头部
如果多个XDomainRequests同时被发送,一些请求可能会丢失,为避免这种情况,xdr.send()的调用应被包裹在setTimeout方法中
参考链接 https://developer.mozilla.org/zh-CN/docs/Web/API/XDomainRequest https://github.com/github/fetch https://github.com/github/fetch/issues/326 https://github.com/github/fetch/issues/214 https://github.com/axios/axios https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/