axios源码学习

0 axios创建实例

axios可以通过create方法创建多个实例,创建的实例对象也可以调用create方法再次创建示例,好处在于可以复用公共的示例对象配置

function createInstance(defaultConfig) {
	instance.create = function create(instanceConfig) {
		return 	createInstance(mergeConfig(defaultConfig,instanceConfig))
	}
	return instance
}

1 判断一个对象是否普通对象

  • Symbol.toStringTag:可以修改Object.prototype.toString.call返回的后缀,普通对象自带该属性,不需要设置,如果设置说明该对象不是普通对象
  • Symbol.iterator:拥有该属性的对象可以使用for of进行遍历,普通对象没有该属性
//Object.propotype 指向null 说明它是最后的Object
//
const isPlainObject = (val) => {
	const str = Object.prototype.toString.call(val)
	const type = str.slice(8,-1).toLowerCase();
	if (type !== 'object') {
		return false	
	}
	//是object
	const prototype = Object.getPropertyOf(val);
	return (prototype === Object.prototype || prototype === null || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val)
	
}

2 遍历对象,使对象每一项执行绑定的函数

const foreach = (obj, fn) => {
	if(obj === null || typeof obj === 'undefined') return;
	let i,l;
	//如果不是对象,使用数组包裹
	if (typeof obj !== 'object' ) {
		obj = [obj]
	}
	//遍历数组
	if (Array.isArray(obj)) {
		for(i=0, l=obj.length; i<l;i++) {
			fn.call(null, obj[i], i)
		}
	} else {
		//对象的所有键名
		const keys = Object.keys(obj)
		for(let i =0; i<keys.length; i++) {
			const key = keys[i]
			fn.call(null, obj[key], key)
		}
	}
}

3 对象继承和改变继承对象的this

const extend = (a: object, b: object, thisArg: object) => {
	foreach(b, (val, key) => {
		// 如果指定了thisArg, 并且键值是一个函数
		if (thisArg && typeof val === 'function') {
			a[key] = val.bind(thisArg);
		} else {
			a[key] = val;	
		}
	})
	return a;
}

4 合并对象属性,相同属性后面的替换前面的

const merge = () {
	let result = {};
	const assignValue = (val, key) => {
		//如果遍历的值为对象类型,则需要深度遍历
		if (typeof val === 'object' ) {
			result[key] = merge(result[key], val)
		} else {
			result[key] = val;
		}
	};
	for(let i = 0; i<arguments.length; i++) {
		foreach(arguments[i], assignValue)
	}
	return result
}

5 request函数

axios的使用如下:
common表示对于任何类型的请求都要添加该属性,method表示只有该类型的请求方法才会添加对应的属性

axios.defaults.headers.common['test'] = 123
axios.defaults.headers.post['Content-Type'] = 'application/x-www/'
//两种形式
//axios('/test')
//axios({pathname: '/test',})

const request = (configOrUrl, config) {
	//首先判断第一个参数是不是url
	if (typeof configOrUrl === 'string' ) {
		config = config || {};	//get请求没有config
		config.url = configUrl	
	} else {
		config = configOrUrl || {}
	}
	//兼容以前的版本在新版本中移除、自定义序列化参数、请求头
	const { transitional, paramsSerializer, headers } = config;
	
	if(paramsSerializer !== null) {
		...	
	}
	
	//设置请求方法
	config.method = (config.method || 'get').toLowerCase();
	
	//需要将headers中共有的属性和私有属性进行合并
	let contextHeaders = merge(headers.common, headers[config.method])
	
	//删除headers中的这些属性
	foreach(['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], (method) => {
		delete headers[method];	
	})
	
	//筛选除跳过的拦截, 由于eject会移除拦截器,但是索引会被保留,因此需要forEach跳过值为null的函数
	const requestInterceptorChain = [];
	let synchronousRequestInterceptors = true;
	//传入runWhen方法,如果runWhen返回值为false则忽略这个拦截器
	this,interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
		if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
			return;	
		}
		//是否同步执行
		synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
		//先执行最后的回调,在执行前面的,因此拦截器添加方式后来的放在数组前面
		requestInterceptorChain.unshift(interceptor.fulfilled,interceptor.rejected)
	})
	
	 const responseInterceptorChain = [];
	 this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
	 	responseInterceptorChain.push(interceptor.fulfilled,interceptor.rejected)
	 })
	
	//异步执行请求拦截器
	let promise;
	let i=0;
	let len;
	if (!synchronousRequestInterceptors) {
		const chain = [dispatchRequest, undefined];
		//将请求拦截器放到chain头部
		Array.prototype.unshift.apply(chain, requestInterceptorChain);
		chain.push.apply(chain,responseInterceptorChain);
		len = chain.length;
		promise - Promise.resolve(config);	
		//组合形成promise执行链	
		while (i<len) {
			promise = promise.then(chain.shift(),chain.shift())	
		}
		return promise
	}

	len = requestInterceptorChain.length;
	
	let newConfig = config;
	i = 0;
	while (i<len) {
		const onFulfilled = requestInterceptorChain[i++];
		const onRejected = requestInterceptorChain[i++];
		try {
			//执行请求拦截器的then方法
			newConfig = onFulfilled(newConfig)
		} catch (error) {
			onRejected.call(this, error);
			break;
		}
	}
	try {
		promise = dispatchRequest(newConfig);	
	} catch (error) {
		return Promise.reject(error);	
	}
	//循环执行所有相应拦截器
	while (responseInterceptorChain.length) {
		promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++])
	}
	return promise
	
}

6 取消请求

const token = axios.CancelToken
const source = token,source()
axios.get('', {
	cancelToken: source.token,
})

source.cancel()
class CancelToken {
	constructor(executor) {	
		if (typeof executor !== 'function') {
			throw new TypeErroe('执行器必须要是一个函数')
		}
		//创建一个pending状态的对象, 将resolvePromise变量指向resolve函数
		let resolvePromise;
		 this.promise = new Promise(function promiseExecutor(resolve) {
		 	resolvePromise = resolve	
		 })
		 //传入cancel函数,在函数内部调用resolvePromise将Promise对象从pending状态变为resolved状态
		 executor(message => {
		 	if (this.reason) return 
		 	this.reason = message
		 	resolvePromise(this.reason)	
		 })
		
	}	
	//不能通过类的实例调用,只能通过类本身调用
	static source() {
		let cancel;
		const token = new CancelToken(function executor(c) {
			cancel = c
		})
		return {
			token,
			cancel	
		}
	}
}

//在xhr中加入代码
const { cancelToken } = config
if (cancelToken) {
	cancelToken.promise.then(reason => {
		request.abort()
		reject(reason)	
	})	
}

7 防御CSRF

axios.get('',{
	xsrfCookieName: 'XSRF-TOKEN',
	xsrfHeaderName: 'X-XSRF-TOKEN',
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值