一、Ajax
Ajax(异步javascript 和 XML),其本身不是一种新技术,而是一种技术合集的新方法,用来处理浏览器与服务器之间的http通信,是一种异步请求的技术。其主要步骤如下:
- 创建XMLHttpRequest实例
- 发出HTTP请求
- 接收服务器传回的数据
- 更新网页数据
XMLHttpRequest对象是 AJAX 的主要接口,用于浏览器与服务器之间的通信,其本身是一个构造函数,可以通过new命令生成实例,新建实例后就可以调用open()方法发出http请求;但是在平时工作中如何用原生js实现http请求代码比较多,因此会使用很多封装好的ajax方法库,如jquery中可以使用$.ajax()发出http请求,但是jquery库比较庞大,如果仅仅是使用ajax请求,可以考虑只专注http请求的库,比如axios
二、axios
axios是基于Promise的http库,适用于浏览器和nodejs;Axios通过Promise实现XHR封装,其中Promise是控制手段,XHR是实际发送Http请求的客户端;axois可以看成是ajax技术的一种具体实现方法;其主要特点是:
- 浏览器端发起XMLHttpRequests请求
- node端发起http请求
- 支持Promise API
- 拦截请求和返回
- 转化请求和返回(数据)
- 取消请求
- 自动转化json数据
- 客户端支持抵御XSRF(跨站请求伪造)
axios处理http请求提供了很多功能,但是其作者觉得jsonp的方式并不友好,所以并不支持jsonp的请求,因此在vue或react中需要再引入jsonp库来支持jsonp请求
具体使用文档可参考:https://github.com/axios/axios
三、fetch API
fetch API提供获取资源的接口,它类似于XHR,但提供了更强大灵活的功能,fetch不是ajax的进一步封装,而是原生的js API,并基于promise设计。
fetch 是一个html5的API,因此在兼容性上会有一些问题。fetch使用的例子如下:
fetch(url).then(function(response) {
return response.json();
}).then(function(data) {
console.log(data);
}).catch(function(e) {
console.log("Oops, error");
});
因为支持率不高,可以引入polyfill:
- 由于 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
- 引入 Promise 的 polyfill: es6-promise
- 引入 fetch 探测库:fetch-detector
- 引入 fetch 的 polyfill: fetch-ie8
- 可选:如果你还使用了 jsonp,引入 fetch-jsonp
- 可选:开启 Babel 的 runtime 模式,现在就使用 async/await
四、关于发送http请求的封装
在vue和react项目中可以将http请求封装管理,会使代码结构更清晰,主要从以下几个方面进行封装request.js
- 与服务端约定状态码,请求头,请求超时等
- 增加错误提示信息如toast或者弹框提示,不同状态码提示不是的响应(异常处理程序)
- 设置请求参数,根据业务必须携带不同参数
- 请求拦截器:根据请求头设定,哪些请求处理可以访问
- 响应拦截器:根据服务端状态码执行不同业务
下面是在使用umi开发时封装的http请求示例:
/**
* 异常处理程序
*/
const errorHandler = (error: { response: Response }): Response => {
const { response } = error;
if (response && response.status) {
const errorText = codeMessage[response.status] || response.statusText;
const { status, url } = response;
notification.error({
key: 'api-request',
message: `请求错误 ${status}: ${url}`,
description: errorText,
});
} else if (!response) {
notification.error({
key: 'api-request',
message: '网络异常',
description: '您的网络发生异常,无法连接服务器',
});
}
return response;
};
/**
* 配置request请求时的默认参数
*/
const request = extend({
errorHandler, // 默认错误处理
credentials: 'include', // 默认请求是否带上cookie
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
requestType: 'form',
params: {
_: Date.now(),
_format: 'json',
},
paramsSerializer(params) {
return stringify(params);
},
});
// 请求拦截器
request.interceptors.request.use((url, { extraOptions, ...options }: RequestOptions) => {
const state = getState();
const { href } = window.location;
const isSamePage = prevHref === href;
const uriName = extraOptions?.paramName ?? url;
const prevRequest =
extraOptions?.usePrevQuery && isSamePage ? state.request?.[uriName] ?? {} : {};
delay(500).then(() => {
prevHref = href;
});
extraOptions!.url = url;
extraOptions!.uriName = uriName;
options.data = { ...prevRequest.data, ...options.data, ...extradata };
if (options.requestType === 'form' && options.data) {
options.data = stringify(options.data);
}
return {
url: (extraOptions?.isServeApi ? proxyPrefix + apiPrefix : apiPrefix) + url,
options: { extraOptions, ...options },
};
});
// 响应拦截器
request.interceptors.response.use(
async (response, { params, data, extraOptions }: RequestOptions) => {
const dispatch = getDispatch();
const { errno, errmsg } = await response?.clone().json?.();
// eslint-disable-next-line @typescript-eslint/camelcase
if (errno === API_CODE.NO_LOGIN) {
// 登录态失效同步到state
dispatch({
type: 'user/updateCurrentUser',
payload: {
isLogin: false,
currentUser: {},
},
});
}
if (!extraOptions?.ignoreErr && errno === API_CODE.JUMP) {
const query = qsParse() as IParams;
const pathname = '/work/task';
const key = query.dpkey;
router.push({
pathname,
query: {
dpkey: key,
},
});
return;
}
if (!extraOptions?.ignoreErr && errno !== API_CODE.OK) {
notification.error({
key: 'api-request',
message: '警告',
description: errmsg,
});
}
// 请求参数写入store
dispatch({
type: 'request/updateUri',
payload: {
[extraOptions!.uriName!]: {
url: extraOptions!.url,
page: getPathname(),
params,
data: parse(data),
status: response.status,
errno,
},
},
});
return response;
},
);
参考文章: