功能设计与抽象粒度
合理的设计是 底层部分:保留对全局封装的影响范围 项目层:保留对页面层的影响能力 页面层:保留对组件层的影响能力
需要考虑的功能点:
- 自定义 headers 添加
- 统一断网/弱网处理
- 接口缓存处理
- 接口统一错误提示
- 接口统一数据处理
- 统一数据层结合
-
axios 设计思想
功能特点:
在浏览器端,使用 XMLHttpRequest 发送请求
- 支持 Node.js 端发送请求
- 支持 Promise API,使用 Promise 风格语法
- 支持请求和响应拦截
- 支持自定义修改请求和返回内容
- 支持请求取消
- 默认支持 XSRF 防御
拦截器思想
赋予了分层开发时借助拦截行为,注入自定义能力的功能
axios 拦截器由以下步骤组成:
// lib/core/Axios.js
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
// lib/core/InterceptorManager.js
function InterceptorManager() {
// 存储所有的拦截器,但请求拦截器和响应拦截器是分开的
this.handlers = [];
}
/**
* 添加拦截器
* fulfilled: 成功时执行的,在Promise.resolve中
* rejected: 失败时执行的,在Promise.reject中
*
* 返回当前添加的拦截器的ID,用于清除这个拦截器
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
// 把传入的在resolve和reject中要执行的方法添加到数组中
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
});
return this.handlers.length - 1;
};
/**
* 根据id请求拦截器
*
* id: 刚才use方法返回的那个数据
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* 迭代所有的拦截器
*
* 这里会跳过之前使用eject方法设置为null的拦截器
*
* @param {Function} fn 对所有拦截器都执行的一个方法
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
任务编排
创建一个 chain 数组,把所有拦截器放进去,然后将请求拦截器插入到实际发送请求的方法前,响应拦截器插入该请求后
// dispatchRequest 用于请求数据,这里我们先展示不管怎么实现的
// 这里把 dispatchRequest 也当做拦截器添加到队列中
// 每2个是一组,前面用于Promise.resolve, 后面的1个用户Promise.reject
var chain = [dispatchRequest, undefined];
// 拦截器调用forEach方法,把每一个请求拦截器都添加到chain的前面
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
// 每2个是一组,前面用于Promise.resolve, 后面的1个用户Promise.reject
// 由此也能看到,越是后添加的请求拦截器,越会是先执行
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 拦截器调用forEach方法,把每一个响应拦截器都添加到chain的后面
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
// 响应拦截器按照顺序执行
chain.push(interceptor.fulfilled, interceptor.rejected);
});
任务调度
通过一个 While 循环,通过一个 Promise 实例,遍历迭代 chain 数组方法,并基于 Promise 回调特性,将各个拦截器串联执行起来。
// 把config初始化为一个Promise对象,方便后面的使用
var promise = Promise.resolve(config);
while (chain.length) {
// 依次取出执行resolve和reject方法
// 将执行后的结果传给下一个拦截器
promise = promise.then(chain.shift(), chain.shift());
}
适配器思想(Adapter)
安全思想
依赖双重 cookie 的方式防御 CSRF