<p>打开network面板可以看到这两个请求的发起方式是不一样的</p>
<p>一个是通过请求js产生的,一个是XMLHttpRequest产生的</p>
function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
}
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
function combineURLs(baseURL, relativeURL) {
return relativeURL ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL;
}
function buildFullPath(baseURL = '', requestedURL = '') {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL);
}
return requestedURL;
}
function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url;
}
var serializedParams;
var parts = [];
for (var key in params) {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
}
serializedParams = parts.join('&');
if (serializedParams) {
var hashmarkIndex = url.indexOf('#');
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex);
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
}
return url;
}
// 这里的config是axios里所有的配置
var jsonpAdapter = (config) => {
return new Promise((resolve, reject) => {
// 是否已取消当前操作
// 因jsonp没有主动取消请求的方式
// 这里使用 isAbort 来标识
var isAbort = false;
// 定时器标识符
var timer = null;
// 执行方法的名字,
var callbackName = `jsonp${Date.now()}_${Math.random().toString().slice(2)}`;
// 这里假设已经实现了baseURL和url的拼接方法
var fullPath = buildFullPath(config.baseURL, config.url);
// 这里假设已经实现了url和参数的拼接方法
// 不太一样的地方在于,jsonp需要额外插入一个自己的回调方法
var url = buildURL(
fullPath,
{
...config.params,
...{ [config.jsonpCallback || 'callback']: callbackName },
},
config.paramsSerializer
);
// 创建一个script标签
var script = document.createElement('script');
// 成功执行操作后
function remove() {
if (script) {
script.onload = script.onerror = null;
// 移除script标签
if (script.parentNode) {
script.parentNode.removeChild(script);
}
// 取消定时器
if (timer) {
clearTimeout(timer);
}
script = null;
}
}
// 成功请求后
window[callbackName] = (data) => {
// 若已需要请求,则不再执行
if (isAbort) {
return;
}
// 返回的格式
var response = {
status: 200,
statusText: 'ok',
config,
request: script,
data: data,
};
remove();
// 实际上这里上一个settle操作,会额外判断是否是合理的status状态
// 若我们在config.validateStatus中设置404是合理的,也会进入到resolve状态
// 但我们这里就不实现这个了
// settle(resolve, reject, response);
resolve(response);
};
// 请求失败
script.onerror = function (error) {
remove();
reject(createError('Network Error', config, 404));
};
// 若设置了超时时间
if (config.timeout) {
timer = setTimeout(function () {
remove();
// 取消当前操作
isAbort = true;
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 405));
}, config.timeout);
}
// 若定义了取消操作
if (config.cancelToken) {
config.cancelToken.promise.then(function () {
if (!script) {
return;
}
remove();
isAbort = true;
reject(createError('Cancel Error', config, 404));
});
}
script.src = url;
var target = document.getElementsByTagName('script')[0] || document.head;
target.parentNode && target.parentNode.insertBefore(script, target);
});
};
var request = axios.create({
adapter: function (config) {
if (config.params && config.params.format === 'jsonp') {
return jsonpAdapter(config);
}
// 这里需要将config.adapter设置为空
// 否则会造成无限循环
return axios(Object.assign({}, config, { adapter: undefined }));
},
});
// 使用自定义的适配器jsonp发起请求
request('https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336', {
params: {
format: 'jsonp',
},
})
.then(function (response) {
console.log('jsonp response', response);
})
.catch(function (error) {
console.error('jsonp error', error);
});
// 使用axios默认的适配器发起请求
request('https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336')
.then(function (response) {
console.log('axios response', response);
})
.catch(function (error) {
console.error('axios error', error);
});