1-axios发送请求-详解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> axios 的由来</title>
<!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
<script>
/*
// 这个写完只能使用 axios.get来触发,还不能使用 axios({}) ????
什么时候可以使用axios() 触发呢???
可以触发, 默认执行的是 request 函数
*/
Axios.prototype.request = function request(config) {
console.log('request', config)
// 预先处理config,合并
}
Axios.prototype.get = function(config){ return this.request({method: 'get'})}
Axios.prototype.post = function(config){ return this.request({method: 'post'})}
function Axios(config) {
this.defaults = config
this.intercepters = {
request: {},
response: {}
}
}
function createIntance(config) {
const context = new Axios(config)
const intance = Axios.prototype.request.bind(context)
// 获取Axios上的原型,挂载到axios上
Object.keys(Axios.prototype).forEach((key) => {
intance[key] = Axios.prototype[key].bind(context)
})
// 获取Axios上的属性,挂载到axios上
Object.keys(context).forEach((key) => {
intance[key] = context[key]
})
return intance
}
const axios = createIntance({method: 'get'})
console.log(axios.get());
console.log(axios({method: 'put'})); // 函数执行,this指向Axios,又因为 Axios.prototype.request使用bind向外保存了一个request函数,所以axios执行就是request函数执行
/*
先说axios发送请求:
首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
说细节在说这里:
Axios.prototype[method] = function (url, config) {
return this.request(
utils.merge(
config || {},
{
method: method,
url: url
}
)
);
};
调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了
axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
*/
</script>
</script>
</body>
</html>
2-axios请求发送
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Axios() {}
/*
1、处理config,自己的跟传入的合并
2、派发请求,需要使用promise接收请求后的值,
3、链式存储
*/
Axios.prototype.request = function(config) {
let promise = Promise.resolve(config)
let chains = [dispatchRequest, undefined]
let result = null
while( chains.length ){
result = promise.then(chains.shift(), chains.shift())
/*
影响原数组
pop 后删, push 后增,影响原数组
shift 前删, unshift 前增,影响原数组
arr[1] = 234 影响原数组
splice(1, 0, 2, 3) 追加数据,影响原数组
不影响原数组。
slice(1, 3) >=1, <=3, 返回新数组,不影响原数组
concat 拼接数组,返回新数组,不影响原数组
*/
}
return result
}
function dispatchRequest(config) {
// 会先判断是http 还是 xhr
// 接收到 xhr发送的请求为promise,所以用then接收
const adapter = xhrAdapter(config)
// 返回的是 成功的 promise,否则在request函数中无法使用.then接收,此时 status = resolved, data = response, 就返回给request中的result
return adapter.then( response => {
return response
}, error => {
console.log(error, '错误的请求')
})
}
function xhrAdapter(config){
return new Promise( (resolve, reject) => {
debugger
let xhrHttp = new XMLHttpRequest()
xhrHttp.open(config.method, config.url)
xhrHttp.send()
// console.log(this, 'this111')
xhrHttp.onreadystatechange = function() {
// console.log(xhrHttp, 'xhrHttp222')
if (xhrHttp.readyState == 4) {
if (xhrHttp.status >= 200 && xhrHttp.status < 300) {
const response = {
msg: '请求成功',
code: '200',
data: {cardUrl: "zQfMZrUnUjAvZvQnE3zmyURn"}
}
resolve(response)
} else {
reject(new Error('请求失败 失败的状态码为' + xhrHttp.status));
}
}
}
})
}
const context = new Axios()
const axios = Axios.prototype.request.bind(context)
axios({
url: 'http://localhost:3000/mies',
method: 'get',
}).then(response => {
console.log('response', response);
})
/*
1、先说axios发送请求:
首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
说细节在说这里:
Axios.prototype[method] = function (url, config) {
return this.request(
utils.merge(
config || {},
{
method: method,
url: url
}
)
);
};
调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了
axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
2、 请求开始发送
function request(config) {
const chians = [dispatch, undefined]
let result = Promise.resolve(config)
while(chians > 0) {
result.promise(chians.shift(0), chians.shift(1))
}
return result
}
function dispatch() {
return adapters.then(
(resData) => {
return response
}
)
}
function adapters() {
return new Promise((resolve, reject) => {
new XMLHttpRequest()
send
open
onreadystatechange
status === 4 && code >= 200 && code < 300
resolve(res.data)
})
}
2.1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data
*/
</script>
</body>
</html>
3.axios拦截器实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
手写axios的拦截器
*/
function Axios(){
this.default = {}
this.interceptors = {
request: new Interceptors(),
response: new Interceptors()
}
}
// 创建axios
function createdInstance(config) {
const context = new Axios(),
instance = Axios.prototype.request.bind(context)
Object.keys(context).forEach((key) => {
instance[key] = context[key]
})
return instance
}
// 调用adapters中的xhr 请求
function dispatchRequest(config){
return new Promise((resolve, reject) => {
resolve({
status: '200',
msg: '请求成功'
})
})
}
Axios.prototype.request = function(config){
let promise = Promise.resolve(config)
// this.interceptors.request = function(response) {
// chains.unshift(response.request, response.response)
// }
// this.interceptors.response = function(response) {
// chains.push(response.request, response.response)
// }
const chains = [dispatchRequest, undefined]
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
debugger
//将请求拦截器压入数组的最前面
chains.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
//将相应拦截器压入数组的最尾部
chains.push(interceptor.fulfilled, interceptor.rejected);
});
while(chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift())
}
return promise
}
// 定义拦截器
function Interceptors() {
this.heardes = []
}
Interceptors.prototype.use = function(fulfilled, rejected) {
this.heardes.push({
fulfilled,
rejected
})
console.log('this.heardes', this.heardes)
}
Interceptors.prototype.forEach = function(fn){
// 简化版本
this.heardes.forEach(item => {
fn.call(null, item)
})
}
const axios = new createdInstance()
console.log('axios', axios)
axios.interceptors.request.use( request => {
console.log('我是request第一个request---use', request);
return request
}, reject => {
console.log('我是request第一个reject---use');
}
)
axios.interceptors.response.use( response => {
console.log('我是request第一个response---use', response);
return response
}, reject => {
console.log('我是request第一个reject---use');
}
)
axios({
method: 'GET',
url: 'http://XXX'
}).then(res => {
console.log('---<res', res);
})
/*
1、如何获取 Fn 函数上的属性值??
2、如果我有一个 Axios 实例,有 interceptors 属性,如何巧妙的调用 Interceptors 实例上的属性
3、axios.interceptors.request.use 拦截器request挂载的时候需要将数据 return 出去
3.1:因为在 while 循环执行的时候,上一个执行结果为 promise, status = resolve, data = config
while(chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift())
}
3.2:使用promise.then执行这个回调函数,如果没有return, 那么 status = resolve , data = undefined
request => {
console.log('我是request第一个request---use', request);
return request
}
3.3:最后axios.then 接收到的数据 就为undefined
axios({
method: 'GET',
url: 'http://XXX'
}).then(res => {
console.log('---<res', res);
})
总结:
function Fn(name, age) {
this.name = name
this.age = age
}
const fn = new Fn('张三', 18)
console.log('fn', fn)
Object.keys(fn).forEach(key => {
console.log('key', key)
})
// 1、如何获取 Fn 函数上的属性值??
可以通过 Object.keys(fn).forEach(key => {
console.log('key', key)
})
// 2、如果我有一个 Axios 实例,有 interceptors 属性,如何巧妙的调用 Interceptors 实例上的属性
function Axios1() {
this.interceptors = {
request: new Interceptors1()
}
}
function Interceptors1() {
function fulfilled() {}
function rejected() {}
this.heardes = [ { fulfilled, rejected }]
}
Interceptors1.prototype.forEach = function (fn){
this.heardes.forEach(item => {
fn.call(null, item)
})
}
const axios1 = new Axios1()
function callBack(interceptor) {
console.log('interceptor', interceptor)
}
axios1.interceptors.request.forEach(callBack)
*/
/*
1、先说axios发送请求:
首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
说细节在说这里:
Axios.prototype[method] = function (url, config) {
return this.request(
utils.merge(
config || {},
{
method: method,
url: url
}
)
);
};
调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了
axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
2、request 请求发送
function request(config) {
const chians = [dispatch, undefined]
let result = Promise.resolve(config)
while(chians > 0) {
result.promise(chians.shift(0), chians.shift(1))
}
return result
}
function dispatch() {
return adapters.then(
(resData) => {
return response
}
)
}
function adapters() {
return new Promise((resolve, reject) => {
new XMLHttpRequest()
send
open
onreadystatechange
status === 4 && code >= 200 && code < 300
resolve(res.data)
})
}
2。1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data
3. 拦截器执行
调用时执行request中的dispatch前先 this.Interceptors.request.use 给Interceptors中的header绑定属性,在新增一个对象,将对象中成功/失败的函数插入到 chians中,开始遍历执行,为什么拦截器之前的顺序会发生改变,因为使用unshift向前插入,执行先执行第一个,请求拦截器执行完后,config的配置发生改变,执行dispatch,在执行响应拦截器,最后返回给then,
*/
</script>
</body>
</html>
04-axios请求取消模拟
/*
如何理解的canToken????
config 上配置 new cancelToken(function(cb) {
cancel = cb
}),
来接收返回的一个回调,回调函数中执行 改变 xhr 中绑定promise的.then,当cancel执行,promise的状态改为 resolve,prmise的成功回调开始执行,然后request.abort()取消请求了
cancelToken(cb) {
let aResolve
this.promise = new Promise((resolve) => {
aResolve = resolve
})
cb(
function(){ aResolve() }
)
}
xhr 中配置的
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
//取消请求
request.abort();
reject(cancel);
});
}
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 模拟发送请求完成,但是需要将网络速度降低,才可以测试
在网络中设置低速3g就可以了
-->
<button id="click1">我来发送请求</button>
<button id="click2">取消发送请求</button>
<script>
function Axios() {}
Axios.prototype.request = function(config) {
debugger
return dispatchRequest(config)
}
function dispatchRequest(config) {
return adapters(config)
}
function adapters(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(config.method, config.url)
xhr.send()
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
setTimeout(() => {
resolve({
code: '200',
msg: '请求成功啦'
})
}, 1000)
} else {
reject(new Error('请求失败啦') )
}
}
}
debugger
if (config.cancelToken) {
config.cancelToken.promise.then(
function tokenResolve() {
xhr.abort()
reject(new Error('请求取消啦'))
},
function cancel(){}
)
}
})
}
const context = new Axios()
const axios = Axios.prototype.request.bind(context)
function CancelToken(executor) {
let resolvePromise;
this.promise = new Promise((resolve) => {
resolvePromise = resolve
})
debugger
executor(
function cancel() {
resolvePromise()
}
)
}
var cancel
function callBack(cancelCallBack) {
cancel = cancelCallBack
console.log('cancelCallBack', cancelCallBack)
}
document.getElementById('click1').onclick = function () {
axios({
method: 'GET',
url: 'http://localhost:3000/posts',
cancelToken: new CancelToken(callBack)
}).then(res => {
cancel = null
console.log(res, '我是请求成功的数据')
})
}
document.getElementById('click2').onclick = function () {
console.log('cancel', cancel())
}
/*
1、先说axios发送请求:
首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
说细节在说这里:
Axios.prototype[method] = function (url, config) {
return this.request(
utils.merge(
config || {},
{
method: method,
url: url
}
)
);
};
调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了
axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
2、request 请求发送
function request(config) {
const chians = [dispatch, undefined]
let result = Promise.resolve(config)
while(chians > 0) {
result.promise(chians.shift(0), chians.shift(1))
}
return result
}
function dispatch() {
return adapters.then(
(resData) => {
return response
}
)
}
function adapters() {
return new Promise((resolve, reject) => {
new XMLHttpRequest()
send
open
onreadystatechange
status === 4 && code >= 200 && code < 300
resolve(res.data)
})
}
2。1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data
3. 拦截器执行
调用时执行request中的dispatch前先 this.Interceptors.request.use 给Interceptors中的header绑定属性,在新增一个对象,将对象中成功/失败的函数插入到 chians中,开始遍历执行,为什么拦截器之前的顺序会发生改变,因为使用unshift向前插入,执行先执行第一个,请求拦截器执行完后,config的配置发生改变,执行dispatch,在执行响应拦截器,最后返回给then,
4. 请求取消
let canFun = null
function callback(fn) {
canFun = fn
}
function CancelToken(executor) {
let resolvePromise;
this.promise = new Promise((resolve) => {
resolvePromise = resolve
})
executor(
function cancel() {
resolvePromise()
}
)
}
function adaptor() {
if (config.cancelToken) {
config.cancelToken.promise.then(
function tokenResolve() {
xhr.abort()
reject(new Error('请求取消啦'))
},
function cancel(){}
)
}
}
在config中添加属性,cancelToken: new cancelToken(callback)传递一个函数,new cancelToken执行接收 fn,其中有 new Promise 执行,将未执行的resolve 保留,fn函数执行后用函数包裹 resolve ,返回给外部函数,点击取消按钮执行,就会走 adaptor中then成功的回调函数,然后执行onResolve函数,会有 reject(throw '取消请求')执行,此次请求就取消了
*/
</script>
</body>
</html>