在实践过程中,我们发现axios源码经常使用,今天我们就自己实现一个axios(不包括nodejs)

axios实现get,post实现

  • 新建一个axios目录,在里面新建index.js
    这里我们需要把自己的axios导出,代码如下:
import Axios from './axios'
const axios = new Axios()
export default axios
  • 接下来新建axios.js文件
class Axios {
	constructor() {},
	get(){},
	post(){}
}
export default Axios

通过以上两步操作,我们能在项目文件(main.js)引入

import axios from './axios'
axios.get('http://localhost:3000/json').then(res => {
  console.log(res)
})

axios.post('http://localhost:3000/data', {
	a: 1,
	b: 2
}).then(res => {
  console.log(res)
})

完善get,post成功请求

通过上面描述,我们已经搭建出基本结构,接下来要实现的就是,如何通过ajax请求,完善get,post方法

  • 继续打开axios/index.js文件,完善get,post方法
/**
get请求
@params
{string} url 请求地址
*/
get(url, params){
	return new Promise(resolve => {
		let xhr = new XMLHttpRequest()
		xhr.onload = function () {
			resolve({
				data: JSON.parse(xhr.responseText),
				status: xhr.status,
				statusText: xhr.statusText
			})
		}
		if(params){
			if(Object.prototype.toString.call(params) === '[object Object]' && Object.keys(configs.params).length > 0){
					url += '?'
					for(let key in params){
						url += key + '='+configs.params[key]+'&'
					}
					var urlGets = url
					url = urlGets.substring(0, urlGets.length - 1)
			}
		}
		xhr.open('get', url, true)
		xhr.send()
	})
}

/**
post请求
@params
{string} url 请求地址
{Object} data 请求数据
*/
post(url, data){
	return new Promise(resolve => {
		let xhr = new XMLHttpRequest()
		xhr.onload = function () {
			resolve({
				data: JSON.parse(xhr.responseText),
				status: xhr.status,
				statusText: xhr.statusText
			})
		}
		xhr.open('post', url, true)
		if(Object.prototype.toString.call(data) === '[object Object]'){
			xhr.send(JSON.stringify(data))
		}
		else{
			xhr.send()
		}
	})
}

通过以上操作,我们在main.js里面就可以用get,post请求了

axios.get('http://localhost:3000/json'),then(res=>{console.log(res)})
axios.post('http://localhost:3000/data',{a: 1, b: 2}),then(res=>{console.log(res)})

完善get,post失败请求

上面只是做了请求成功时状态,请求失败还没有,接下来就完善这一部分功能,完善之后的方法如下:

axios.get('http://localhost:3000/json').then(
	res=>{console.log(res)
}).catch(err =>{
	console.log(err)
})
axios.post('http://localhost:3000/data',{
	a: 1, 
	b: 2
}).then(
	res=>{console.log(res)
}).catch(
	err =>{console.log(err)
})
/**
get请求
@params
{string} url 请求地址
{Object} params get请求数据
*/
get(url, params){
   return new Promise(resolve => {
   	let xhr = new XMLHttpRequest()
   	xhr.onload = function () {
   		resolve({
   			data: JSON.parse(xhr.responseText),
   			status: xhr.status,
   			statusText: xhr.statusText
   		})
   	}
   	if(params){
   			if(Object.prototype.toString.call(params) === '[object Object]' && && Object.keys(configs.params).length > 0){
   				url += '?'
   				for(let key in params){
   					url += key + '='+configs.params[key]+'&'
   				}
   				var urlGets = url
   				url = urlGets.substring(0, urlGets.length - 1)
   			}
   	}
   	xhr.open('get', url, true)
   	xhr.send()
   })
}

/**
post请求
@params
{string} url 请求地址
{Object} data 请求数据
*/
post(url, data){
   return new Promise((resolve, reject) => {
   	let xhr = new XMLHttpRequest()
   	xhr.onload = function () {
   		if(xhs.status == 200 || xhr.status == 304 || xhr.status == 100){
   			resolve({
   				data: JSON.parse(xhr.responseText),
   				status: xhr.status,
   				statusText: xhr.statusText
   			})
   		}
   		else{
   			reject({
   				data: xhr.responseText,
   				status: xhr.status,
   				statusText: xhr.statusText
   			})
   		}
   	}
   	xhr.open('post', url, true)
   	if(Object.prototype.toString.call(data) === '[object Object]'){
   		xhr.send(JSON.stringify(data))
   	}
   	else{
   		xhr.send()
   	}
   })
}

通过以上操作,我们在main.js里面就可以用get,post请求了

axios.get('http://localhost:3000/json'),then(res=>{console.log(res)})
axios.post('http://localhost:3000/data',{a: 1, b: 2}),then(res=>{console.log(res)})

axios.create

通过上面操作,我们完成了axios.get,axios.post请求,但是再使用axios方法中,我们发现还有axios.create方法,实现它的思路如下:

axios/index.js

// ---其他代码
axios.create = function (config) {
	return new Axios(config)
}
// ---其他代码

axios/axios.js

constructor(){
	this.defaults =  = copyObject(config) || {}
}
get(url, config){
	// --- 其他代码
	let configs = mergeConfig(this.defaults, config);
	xhr.open('get', configs.baseURL + url, true)
	//添加header头
	for(let key in configs.headers){
		xhr.setRequestHeader(key,configs.headers[key])
	}
	// --- 其他代码
}
post(url, config){
	// --- 其他代码
	let configs = mergeConfig(this.defaults, config);
	xhr.open('post', configs.baseURL + url, true)
	//添加header头
	for(let key in configs.headers){
		xhr.setRequestHeader(key, configs.headers[key])
	}
	// --- 其他代码
}

这里注意,我们需要把传入的对象进行拷贝,防止对其他数据产生影响

在axios目录下,新建utils文件

// 深拷贝
export function copyObject(orig) {
	if(!orig){
		return {}
	}
	var copy = Object.create(Object.getPrototypeOf(orig));
	copyOwnPropertiesFrom(copy, orig);
	return copy;
}

function copyOwnPropertiesFrom(target, source) {
	Object
		.getOwnPropertyNames(source)
		.forEach(function (propKey) {
			var desc = Object.getOwnPropertyDescriptor(source, propKey);
			Object.defineProperty(target, propKey, desc);
		});
	return target;
}

// 对象合并
export function mergeConfig (obj1, obj2) {
	let target = copyObject(obj1),
		source = copyObject(obj2);
	
	return Object.keys(source).reduce((t,k)=>{
		if(['url','baseURL','method', 'data', 'params'].includes(k)){
			t[k] = source[k]
		}
		if(['headers'].includes(k)){
			t[k] = Object.assign({},source[k],t[k])
		}
		return t;
	},target)
}

通过以上操作,我们可以通过axios.create方法创建axios

let instance = axios.create({
  baseURL: 'http://localhost:3000',
  headers : {
    token : 'x-token-123456'
  }
})
instance.get('http://localhost:3000/json'),then(res=>{console.log(res)})
instance.post('http://localhost:3000/data',{a: 1, b: 2}),then(res=>{console.log(res)})

axios拦截方法

在axios里面,我们一般通过如下方法进行拦截

axios.interceptors.request.use(function (config) {
  // Do something before request is sent
  return config;
}, function (error) {
  // Do something with request error
  return Promise.reject(error);
});
axios.interceptors.response.use(function (config) {
  // Do something before request is sent
  return config;
}, function (error) {
  // Do something with request error
  return Promise.reject(error);
});

通过以上方法,我们可以猜想,axios.interceptors实现,它应该是在axios里面,通过constructor方法引入的一个属性,类型是一个对象,request,response应该是interceptors对象的两个属性,use我们可以假设它是request,reponse, new Interceptor()的两个方法

  • axios/axios.js
constructor(config) {
		// -- 其他代码
		//拦截器
		this.interceptors = {
			request: new Interceptor(),
			response : new Interceptor()
		}
		// -- 其他代码
}
  • axios目录下新建Interceptor.js文件
export default class Interceptor{
	constructor() {
		this.handlers = []
	}
	use(resolvedHandler, rejectedHandler){
		this.handlers.push({
			resolvedHandler,
			rejectedHandler
		})
	}
}

这里我们设计成数组,是因为可以多次use(axios.interceptors.request.use)

  • 接下来在axios/axios.js里面新建requse,send方法
//request请求
request(config){
	let configs = mergeConfig(this.defaults, config);
	let promise = Promise.resolve(configs)
	//请求拦截器,遍历 interceptors.request 里的处理函数
	let requestHandlers = this.interceptors.request.handlers;
	requestHandlers.forEach(handler => {
		promise = promise.then(handler.resolvedHandler, handler.rejectedHandler)
	})
	// 通过handler.resolvedHandler方法返回配置好的config(
	//axios.interceptors.request.use(config=>{
	  //console.log('请求配置信息:',config);
	  //return config
	//})
//),调用send方法完成请求
	promise = promise.then(this.send)
	
	//相应拦截器,遍历 interceptors.response 里的处理函数
	let responseHandlers = this.interceptors.response.handlers;
	responseHandlers.forEach(handler => {
		promise = promise.then(handler.resolvedHandler, handler.rejectedHandler)
	})
	return promise;
}
  • 将请求逻辑拆出来,命名为send方法
send(configs){
	return new Promise((resolve, reject) => {
		let xhr = new XMLHttpRequest()
		xhr.onload = function () {
			if(xhr.status == 200 || xhr.status == 304 || xhr.status == 100){
				resolve({
					data: JSON.parse(xhr.responseText),
					status: xhr.status,
					statusText: xhr.statusText
				})
			}
			else{
				reject({
					data: xhr.responseText,
					status: xhr.status,
					statusText: xhr.statusText
				})
			}
			
		}
		if(configs.method.toLowerCase() == 'get'){
			var urlGet = configs.baseURL + configs.url
			if(configs.params){
				if(Object.prototype.toString.call(configs.params) === '[object Object]' && Object.keys(configs.params).length > 0){
					urlGet += '?'
					for(let key in configs.params){
						urlGet += key + '='+configs.params[key]+'&'
					}
					var urlGets = urlGet
					urlGet = urlGets.substring(0, urlGets.length - 1)
				}
			}
			xhr.open('get', urlGet, true)
		}
		else{
			xhr.open('post', configs.baseURL + configs.url, true)
		}
		//添加header头
		for(let key in configs.headers){
			xhr.setRequestHeader(key,configs.headers[key])
		}
		if(configs.method.toLowerCase() == 'get') xhr.send()
		if(configs.method.toLowerCase() == 'post') {
			if(Object.prototype.toString.call(configs.data) === '[object Object]'){
				xhr.send(JSON.stringify(configs.data))
			}
			else{
				xhr.send()
			}
		}
	})
}
  • 更改get,post请求方法
get(url, config){
	if(!config){
			config = {}
	}
	config.method = 'get';
	config.url = url;
	return this.request(config);
}
post(url, config){
	if(!config){
			config = {}
	}
	config.method = 'post';
	config.url = url;
	return this.request(config);
}

在项目目录main.js通过如下方法调用

//请求拦截器
axios.interceptors.request.use(config=>{
  //console.log('请求配置信息:',config);
  return config
})

axios.interceptors.request.use(config=>{
  config.headers.token = 'my token';
  config.baseURL='http://localhost:3000'
  return config
})

//响应拦截器
axios.interceptors.response.use(res=>{
  //console.log('请求响应信息',res)
  return res;
})
//
axios.interceptors.response.use(res=>{
  res.msg = 'request is ok ~';
  return res;
})

axios.get('/json', {
  params:{
    a: 1,
    b: 2
  }
}).then(() => {
  //console.log(res)
})

axios.post('/user/auth', {
  data: {
    sign: 'aweyuyuwqueyqwuyeyu'
  }
}).then((res) => {
  console.log(res)
})

axios.js整体代码如下:
axios/axios.js

import {copyObject, mergeConfig} from './utils'
import Interceptor from './Interceptor'
class Axios {
	constructor(config) {
		this.defaults = copyObject(config) || {}
		//拦截器
		this.interceptors = {
			request: new Interceptor(),
			response : new Interceptor()
		}
	}
	get(url, config){
		if(!config){
			config = {}
		}
		config.method = 'get';
		config.url = url;
		return this.request(config);
	}
	post(url, config){
		if(!config){
			config = {}
		}
		config.method = 'post';
		config.url = url;
		return this.request(config);
	}
	//request请求
	request(config){
		let configs = mergeConfig(this.defaults, config);
		let promise = Promise.resolve(configs)
		//请求拦截器,遍历 interceptors.request 里的处理函数
		let requestHandlers = this.interceptors.request.handlers;
		requestHandlers.forEach(handler => {
			promise = promise.then(handler.resolvedHandler, handler.rejectedHandler)
		})
		// 数据请求
		promise = promise.then(this.send)
		
		//相应拦截器,遍历 interceptors.response 里的处理函数
		let responseHandlers = this.interceptors.response.handlers;
		responseHandlers.forEach(handler => {
			promise = promise.then(handler.resolvedHandler, handler.rejectedHandler)
		})
		
		return promise;
	}
	
	// 发送请求
	send(configs){
		return new Promise((resolve, reject) => {
			let xhr = new XMLHttpRequest()
			xhr.onload = function () {
				if(xhr.status == 200 || xhr.status == 304 || xhr.status == 100){
					resolve({
						data: JSON.parse(xhr.responseText),
						status: xhr.status,
						statusText: xhr.statusText
					})
				}
				else{
					reject({
						data: xhr.responseText,
						status: xhr.status,
						statusText: xhr.statusText
					})
				}
				
			}
			if(configs.method.toLowerCase() == 'get'){
				var urlGet = configs.baseURL + configs.url
				if(configs.params){
					if(Object.prototype.toString.call(configs.params) === '[object Object]' && Object.keys(configs.params).length > 0){
						urlGet += '?'
						for(let key in configs.params){
							urlGet += key + '='+configs.params[key]+'&'
						}
						var urlGets = urlGet
						urlGet = urlGets.substring(0, urlGets.length - 1)
					}
				}
				xhr.open('get', urlGet, true)
			}
			else{
				xhr.open('post', configs.baseURL + configs.url, true)
			}
			//添加header头
			for(let key in configs.headers){
				xhr.setRequestHeader(key,configs.headers[key])
			}
			if(configs.method.toLowerCase() == 'get') xhr.send()
			if(configs.method.toLowerCase() == 'post') {
				if(Object.prototype.toString.call(configs.data) === '[object Object]'){
					xhr.send(JSON.stringify(configs.data))
				}
				else{
					xhr.send()
				}
			}
		})
	}
}
export default Axios

运行结果

get请求

axios 实例config axios实现_vue.js


get请求结果

axios 实例config axios实现_vue.js_02


post请求

axios 实例config axios实现_vue.js_03


post请求结果

axios 实例config axios实现_webpack_04