导语
最近,看了黄奕大神的typescript重构axios,并自己亲自实践了一遍。实践是检验真理的唯一标准。
记录一下axios中用到的骚操作
一、使用Promise数组实现拦截器
axios中可以实现请求还有响应的拦截器。如下 :
// use方法接收两个函数,并使用一个对象包裹存入自身
// use(
// resolved,
// rejected
// )
instance.interceptors.request.use(
function(config: any) {
// 发送请求前做什么事
return config;
},
function(error: any) {
// 请求错误时做些事
return Promise.reject(error);
}
);
instance.interceptors.response.use(
(response: any) => {
return response;
},
(error: any) => {
return Promise.reject(error);
}
);
axios内部原理:
const chain = [
{
resolved: dispatchRequest, // 这是一个发送XMLHttpRequest的方法,并在onreadystatechange下resolve response的
rejected: undefined
}
]
const promise = Promise.resolve(config)
interceptors.request.forEach(interceptor=>{
chain.unshift(interceptor)
})
interceptors.response.forEach(interceptor=>{
chain.push(interceptor)
})
// 上面这个操作可以将chain 变为以下数据结构
// [request拦截器2,requset拦截器1,xhr请求,response拦截器1,response拦截器2]
let promise = Promise.resolve(config)
while (chain.length) {
const { resolved, rejected } = chain.shift()
promise = promise.then(resolved, rejected)
}
总结 : 就是数组结合promise.then
不断将数据传递。
二、深合并deepMerge
deepMerge接收多个对象,然后返回一个结合这多个对象的船新版本对象
export function isPlainObject(val: any): val is Object {
return toString.call(val) === '[object Object]'
}
export function deepMerge(...objs: any[]) {
const result = Object.create(null) // 使用Object.create(null) 创造的对象没有原型,所以比较节省内存
// 遍历每一个入参对象
objs.forEach(obj => {
if (obj) {
Object.keys(obj).forEach(key => {
const val = obj[key]
// 判断该属性是不是一个对象
if (isPlainObject(val)) {
// 判断result该属性是否已存在
if (isPlainObject(result[key])) {
// 将其继续合并
result[key] = deepMerge(result[key], val)
} else {
result[key] = deepMerge({}, val)
}
} else {
result[key] = val
}
})
}
})
return result
}
总结: 深合并不是真合并,而是对传入的每个对象进行深拷贝,递归太难想了 - - !
三、如何判断一个URL是否与主域名一致
// 同域名判断
// 通过传入的url,将其设置为一个a标签的url,然后获取该DOM的protocol(协议)、host(域名)属性
// 传入url可以通过 axios({url:''})拿到
// 当前页面的url可以通过window.location.href拿到
export function isURLSameOrigin(requestURL: string): boolean {
const parsedOrigin = resolveURL(requestURL)
return (
parsedOrigin.protocol === currentOrigin.protocol && parsedOrigin.host === currentOrigin.host
)
}
const urlParsingNode = document.createElement('a')
const currentOrigin = resolveURL(window.location.href)
function resolveURL(url: string): URLOrigin {
urlParsingNode.setAttribute('href', url)
const { protocol, host } = urlParsingNode
return {
protocol,
host
}
}
四、使用Promise当一个状态机
该方法是在axios 的CancelToken
使用。
let cancel
// 实际上这种xhr是异步发送的 也就是包裹了promise发送的
axios({
method: 'get',
url: '/cancel/get',
data: {
a: 1,
b: 2
},
CancelToken : new CancelToken(c=> {cancel = c })
})
// 所以这里必须使用setTimeout
setTimeout(() => {
console.log(cancel('取消了异步请求'))
});
let c
const promise = new Promise(resovle=>{
c = resolve
})
promise.then(()=> {
console.log('存入一个回调了')
})
promise.then(()=> {
console.log('存入一个回调了')
})
promise.then(()=> {
console.log('存入一个回调了')
})
c();
原理 : 将Promise
的excutor函数的入参使用闭包或者其他手段缓存,当使用then
往该promise实例的onResolveCallbacks
存入回调函数后,再调用resolve()
将其触发。
网友评论