XmlHttpRequest & Ajax & Fetch & Axios
[注:Image by Birgit Böllinger from Pixabay]
XmlHttpRequest
var xhr=new XMLHttpRequest();
xhr.open("GET",url);
xhr.responseType="json";
xhr.onload=function(){
console.log(xhr.response);
}
xhr.onerror=function(){
console.log("error");
}
xhr.send();
- 在不重新加载页面的情况下更新页面
- 在页面已加载后从服务器请求/接收数据
- 在后台向服务器发送数据
ajax
$.ajax({
type:"post",
url:url,
data:data,
dataType:dataType,
success:function(){},
error:function(){}
})
- 对原生 XHR 的封装
- 增加了对 JSONP 的支持
缺点:
- 针对 MVC 编程,不符合前端 MVVM 的浪潮
- 架构不清晰,如果有多个请求或者依赖关系,容易形成回调地狱
- 为了 ajax 引入 jQuery 不合理
axios
axios({
method:"post",
url:url,
data:data,
}).then(response=>{
console.log(response);
}).catch(err=>{
console.log(err);
})
- 从 node.js 创建 http 请求
- 也是对原生 XHR 的封装,但是是 Promise 的实现版本,符合最新 ES 规范
- 客户端支持防止 CSRF
- 提供了一些并发请求的接口
缺点:
- 只支持现代浏览器
fetch
fetch("/users.json",{
method:"post",
mode:"no-cors",
data:{}
}).then(function(response){
return response.json();
}).then(function(data){
console.log(data);
}).catch(function(e){
console.log("error");
})
换成更简洁的箭头函数的写法:
fetch("/users.json",{
method:"post",
mode:"no-cors",
data:{}
}).then(response=>{
return response.json();
}).then(data=>{
console.log(data);
}).catch(e=>{
console.log("error");
})
换成 es7 async/await 的写法:
try{
let response=await fetch(url);
let data=await response.json();
console.log(data);
}catch(e){
console.log("error",e);
}
Promise, generator/yield,await/async 都是现在和未来JS解决异步的标准做法,可以完美搭配使用。
总结,Fetch 优点:
- 语法简洁
- 基于标准 Promise 实现,支持 async/await
- 因为旧版浏览器不支持 Promise,所以需要使用 polly-fill es6-promise
- 同构方便
- 对跨域的处理
- 在配置中添加
mode:"no-cors"即可
- 在配置中添加
用 Fetch 遇到的坑:
-
Fetch 请求默认不带 cookie ,需要设置
fetch(url,{credentials:"include"}) -
服务器返回 400,500 并不会 reject,只有网络错误导致请求不能完成才会被 reject
-
不能中断,没有 abort, terminate, onTImeout, cancel 方法
-
不同于 XHR ,不能监测请求进度(但这样会比较简单)
我看了我们目前几个项目,都是基于 fetch 实现的,所以看了一下对上述坑的解决方案:
import { message, Modal } from "antd";
import { getLocal } from "common/LocaleProvider";
import omitEmpty from "omit-empty";
//import { removeUserInfo } from "utils/user";
import { getSearchParamValue } from "./url";
function getApiDomain() {
const apiip = getSearchParamValue("apiip");
return apiip || window.location.host;
}
function checkStatus(response: Response): Response {
if (!response.ok) {
const error = new Error(response.statusText);
switch (response.status) {
case 401:
window.location.pathname = "/login.html";
break;
case 500:
message.warn("server error");
break;
default:
return response;
}
throw error;
}
return response;
}
export function getApiUrl(method: string) {
method = method.replace(/\./g, "/");
return `//${getApiDomain()}/api/${method}`;
}
export function doFetch(
url: string,
params: any,
fetchMethod?: "POST" | "GET",
headers?: HeadersInit
) {
const method = fetchMethod ? fetchMethod : "post";
params = omitEmpty(params);
return fetch(url, {
method,
credentials: "include",
headers,
body: JSON.stringify(params)
}).then(checkStatus);
}
const XLocale = new Headers({
"x-locale": getLocal()
});
export function webapi<T>(method: string, params: any, apiURL?: string): Promise<T> {
let url = apiURL ? apiURL : getApiUrl(method);
// return doFetch(url, params)
return doFetch(url, params, "POST", XLocale)
.then(
response =>
response.json().then(json => ({
data: json,
status: response.status
})) as Promise<{
data: any;
status: number;
}>
)
.then<T>(response => {
if (response.status === 200 && response.data.result.code !== 0) {
if (response.data.result.code === 1000) {
removeUserInfo();
parent.location.href = "./login.html";
}
let msg = response.data.result.message;
if (response.data.result.code === 500) {
msg = msg ? msg : "server error";
}
console.error(msg);
console.log(msg);
return Promise.reject({
status: 5001, // 专门为后端设置的专门接口信息错误
message: msg
});
}
if (response.status !== 200) {
return Promise.reject({
status: response.status,
message: "后端接口信息:" + response.data.message
});
}
return response.data;
})
.catch(err => {
const msg = err.message || "";
return Promise.reject({
status: 5002,
message: msg.match(/Failed to fetch/gi) ? `网络不稳定,请稍后重试` : msg
// `接口:${method}挂了或者没有开启CORS跨域功能,请稍等!`
});
});
}
参考
- https://segmentfault.com/a/1190000012836882