axios可以通过create方法创建多个实例,创建的实例对象也可以调用create方法再次创建示例,好处在于可以复用公共的示例对象配置
function createInstance(defaultConfig) {
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig,instanceConfig))
}
return instance
}
Object.prototype.toString.call
返回的后缀,普通对象自带该属性,不需要设置,如果设置说明该对象不是普通对象//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)
}
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)
}
}
}
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;
}
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
}
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
}
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)
})
}
axios.get('',{
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
})