重写axios核心原理

1.从基本使用去分析axios

// 方式一:
axios({
	method: 'get',
	url: 'http://localhost:5000/getTest'
})

// 方式二:
axios.get('http://localhost:5000/getTest')

结论:axios可以通过传进去一个配置对象去发送ajax,或者通过axios.xxx去发起ajax。

2.实现axios

实现axios({})这种形式:

class MyAxios{
	request(config) {
		return this.sendAjax(config)
	}
	sendAjax(config) {
		return new Promise((resolve, reject) => {
			// 原生的ajax
			const {method = 'get', url= '', data= {}} = config
			const xhr = new XMLHttpRequest()
			xhr.onload = ()=>{
				resolve(xhr.response)
			}
			xhr.open(method, url, true)
			xhr.send(data)
		})
	}
}

// 创建axios的function
function createAxiosFn() {
	const axios = new MyAxios()
	// 将axios上的request方法赋值给req
	const req = axios.request.bind(axios)
	return req
}
const axios = createAxiosFn()
axios({
  method: 'get',
  url: 'http://localhost:5000/getTest',
}).then((res) => {
  console.log(res);  // 打印出来就成功了
});

实现axios.xxx方式:

class MyAxios{
	request(config) {
		return this.sendAjax(config)
	}
	sendAjax(config) {
		return new Promise((resolve, reject) => {
			const {method = 'get', url= '', data= {}} = config
			const xhr = new XMLHttpRequest()
			xhr.onload = ()=>{
				resolve(xhr.response)
			}
			xhr.open(method, url, true)
			xhr.send(data)
		})
	}
}

// 常用的http请求方法
const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']
methods.forEach(mt => {
	MyAxios.prototype[mt] = function() {
		// 这四种http请求方法没有data
		const _arr = ['get', 'head', 'options', 'delete']
		if(_arr.includes(mt)) {
			return this.request({
				method: mt,
				url: arguments[0],
				...(arguments[1] || {})
			}) 
		} else {
			return this.request({
				method: mt,
				url: arguments[0],
				data: arguments[1],
				...(arguments[2] || {})
			})
		}
	}
})

// 通用的继承方法, target:目标对象, origin:复制对象 ctx:this
function extend(target, origin, ctx) {
	Object.keys(origin).forEach(key => {
		if(origin.hasOwnProperty(key)) {
			if(typeof origin[key] === 'function') {
				target[key] = origin[key].bind(ctx)
			} else {
				target[key] = origin[key]
			}
		}
	})
}

function createAxiosFn() {
	const axios = new MyAxios()
	const req = axios.request.bind(axios)
	// req继承MyAxios.prototype上的属性,this执行axios
	extend(req, MyAxios.prototype, axios)
	return req
}
const axios = createAxiosFn()

axios.get('http://localhost:5000/getTest').then((res) => {
  console.log(res); // 打印则代表成功
});

3.实现axios的拦截器

// 拦截器的管理器
class InterceptorManage {
  constructor() {
    this.handlers = []; 
  }
  use(fulfilled, rejected) {
  	// 存储拦截器的两个方法,用对象存储
    this.handlers.push({
      fulfilled,
      rejected,
    });
  }
}

class MyAxios {
  constructor() {
    this.interceptor = {
      request: new InterceptorManage(),
      response: new InterceptorManage(),
    };
  }
  request(config) {
    const chains = [this.sendAjax.bind(this), undefined];

    this.interceptor.request.handlers.forEach((hd) => {
      chains.unshift(hd.fulfilled, hd.rejected);
    });

    this.interceptor.response.handlers.forEach((hd) => {
      chains.push(hd.fulfilled, hd.rejected);
    });
    
    let promise = Promise.resolve(config);
    while (chains.length > 0) {
      promise = promise.then(chains.shift(), chains.shift());
    }

    return promise;
  }
  sendAjax(config) {
    return new Promise((resolve, reject) => {
      const { method = 'get', url = '', data = {} } = config;
      const xhr = new XMLHttpRequest();
      xhr.onload = () => {
        resolve(xhr.response);
      };
      xhr.open(method, url, true);
      xhr.send(data);
    });
  }
}

const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
methods.forEach((mt) => {
  MyAxios.prototype[mt] = function () {
    const _arr = ['get', 'head', 'options', 'delete'];
    if (_arr.includes(mt)) {
      return this.request({
        method: mt,
        url: arguments[0],
        ...(arguments[1] || {}),
      });
    } else {
      return this.request({
        method: mt,
        url: arguments[0],
        data: arguments[1],
        ...(arguments[2] || {}),
      });
    }
  };
});

function extend(target, origin, ctx) {
  Object.keys(origin).forEach((key) => {
    if (origin.hasOwnProperty(key)) {
      if (typeof origin[key] === 'function') {
        target[key] = origin[key].bind(ctx);
      } else {
        target[key] = origin[key];
      }
    }
  });
}

function createAxiosFn() {
  const axios = new MyAxios();
  const req = axios.request.bind(axios);
  extend(req, MyAxios.prototype, axios);
  extend(req, axios, axios);
  return req;
}
const axios = createAxiosFn();

axios.interceptor.request.use(
  (config) => {
    console.log('请求拦截');
    return config;
  },
  (err) => {
    return err;
  }
);

axios.interceptor.response.use(
  (res) => {
    console.log('response拦截');
    return res;
  },
  (err) => {
    return err;
  }
);

axios.get('http://localhost:5000/getTest').then((res) => {
  console.log(res);
});

拦截器的工作原理:
在这里插入图片描述
每一个拦截器就是一个promise,只不过只有发送ajax的那个需要耗费久一点的时间而已。我们每axios.interceptor.xxx.use就会往chains这个数组插入一个拦截器。

	// 初始化chains数组,让发送ajax的promise位于中间
    const chains = [this.sendAjax.bind(this), undefined];

	// request的拦截器就放在发送ajax的前面
    this.interceptor.request.handlers.forEach((hd) => {
      chains.unshift(hd.fulfilled, hd.rejected);
    });
	
	// response的拦截器就放在发送ajax的后面
    this.interceptor.response.handlers.forEach((hd) => {
      chains.push(hd.fulfilled, hd.rejected);
    });
    
    // 这里形成我们上面那幅图的结构
    let promise = Promise.resolve(config); // 起点
    while (chains.length > 0) {
      promise = promise.then(chains.shift(), chains.shift()); // 中间的promise
    }

    return promise; // 能获取结果的promise

收获:看了源码,只能更好的使用和理解axios。axios的拦截器的实现思路真的很棒,利用promise的链子一条一条的传下去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值