axios源码分析
axios和Axios的关系?
- 从语法上来说axios不是Axios的实例
- 从功能上说axios是Axios的实例
- axios是Axios.request函数bind()返回的函数
- axios作为对象有Axios原型对象上所有方法,有Axios对象上所有属性
instance与axios的区别
- 相同:
- 都是一个能发任意请求的函数:request(config)
- 都有发特定请求的方法:get()/post()/put()/delete()
- 都有默认配置和拦截器的属性:defaults/interceptors
- 不同:
- 默认配置可能不一样
- instance没有axios后面添加的一些方法:create()/CancelToken()/all()
axios的运行整体流程
axios的请求/响应拦截器是什么
- 请求拦截器:
- 在真正发送请求前执行的回调函数
- 可以对请求进行检查或配置进行特定处理
- 成功的回调函数,传递的默认是config
- 失败的回调函数,传递的默认是error
- 响应拦截器:
- 在请求得到响应后执行的回调函数
- 可以对响应函数进行特定处理
- 成功的回调函数,传递的默认是response
- 失败的回调函数,传递的默认是error
axios的请求/响应数据转换器是什么?
- 请求转换器:对请求头和请求体数据进行特定处理的函数
if(utils.isObject(data)){
setContentTypelfUnset(headers,‘application/json;charset=utf-8’)
return JSON.stringify(data)
}
- 响应转换器:将响应体json字符串解析为js对象或数组的函数
response.data = JSON.parse(response.data)
response的整体结构
- data
- status
- statusText
- headers
- config
- request
error的整体结构
- message
- response
- request
如何取消未完成的请求
- 当配置了cancelToken对象时,保存cancel函数
- 创建一个用于将来中断请求的cancelPromise
- 并定义了一个用于取消请求的cancel函数
- 将cancel函数传递出来
- 调用cancel()取消请求
- 执行cancel函数,传入错误信息message
- 内部会让cancelPromise变为成功,且成功的值为一个Cancel对象
- 在cancelPromise的成功回调中中断请求,并让发请求的promise失败,失败的reason为Cancel对象
模拟实现axios的创建过程
<script>
// 构造函数
function Axios(config) {
// 初始化
this.defaults = config //为了创建defaults默认属性
this.intercepters = {
request: {},
response: {}
}
}
// 原型添加相关的方法
Axios.prototype.request = function(config) {
console.log('发送ajax请求,请求的类型为' + config.method)
}
Axios.prototype.get = function(config) {
return this.request({method: 'GET'})
}
Axios.prototype.post = function(config) {
return this.request({method: 'POST'})
}
// 声明函数
function createInstance(config) {
// 实例化一个对象
let context = new Axios(config) // context.get() 但是不能当作函数使用
// 创建请求函数
let instance = Axios.prototype.request.bind(context) // instance 是一个函数,并且可以instace({}) 此时instace.post 还不能使用
// 将Axios.prototype中对象的方法添加到instance函数对象中
Object.keys(Axios.prototype).forEach(key => {
instance[key] = Axios.prototype[key].bind(context)
})
// 为instance 函数对象添加属性 defaults 与 interceptors
Object.keys(context).forEach(key => {
instance[key] = context[key]
})
console.dir(instance)
return instance
}
let axios = createInstance()
axios({method: 'POST'})
axios.get({})
axios.post({})
</script>
模拟实现axios发送请求的过程
<script>
// 1.声明构造函数
function Axios(config) {
this.config = config
}
Axios.prototype.request = function(config) {
// 发送请求
// 创建一个promise对象
let promise = Promise.resolve(config)
// 声明一个数组
let chains = [dispatchRequest, undefined] // undefiend 占位
// 调用then方法指定回调
let result = promise.then(chains[0], chains[1])
// 返回promise的结果
return result
}
// 2.dispatchRequest函数
function dispatchRequest(config) {
// console.log('dispatchRequest函数')
// 调用适配器发送请求
return xhrAdapter(config).then(response => {
console.log(response)
// 响应的结果进行转换处理
return response
}, error => {
console.log(error)
throw error
})
}
// 3.adapter 适配器
function xhrAdapter(config) {
return new Promise((resolve,reject) => {
// 发送ajax请求
let 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){
// 成功的状态
resolve({
// 配置对象
config: config,
// 响应体
data: xhr.response,
// 响应头
headers: xhr.getAllResponseHeaders(),
// xhr请求对象
request: xhr,
// 响应状态码
status: xhr.status,
// 响应状态自字符串
statusText: xhr.statusText
})
}else{
// 失败的状态
reject(new Error('请求失败'))
}
}
}
})
}
// 4.创建axios函数
let axios = Axios.prototype.request.bind(null)
axios({
method: 'GET',
url: 'http://localhost:3000/posts'
}).then(response => {
console.log(response)
})
</script>
模拟实现axios拦截器
<script>
// 构造函数
function Axios(config) {
this.config = config
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
// 发送请求
Axios.prototype.request = function(config) {
// 创建一个promise对象
let promise = Promise.resolve(config)
// 创建一个数组
const chains = [dispatchRequest, undefined]
// 处理拦截器
// 请求拦截器 将请求拦截器的回调,压入到chains前面
this.interceptors.request.handlers.forEach(item => {
chains.unshift(item.fulfilled, item.rejected)
})
// 响应拦截器
this.interceptors.response.handlers.forEach(item => {
chains.push(item.fulfilled, item.rejected)
})
console.log(chains)
// 遍历
while(chains.length > 0){
promise = promise.then(chains.shift(),chains.shift())
}
return promise
}
// 发送请求
function dispatchRequest(config){
// 返回一个promise队形
return new Promise((resolve, reject) => {
resolve({
status: 200,
statusText: 'OK'
})
})
}
// 创建实例
let context = new Axios({})
// 创建axios函数
let axios = Axios.prototype.request.bind(context)
// 将context属性 config interceptors 添加至axios函数对象身上
Object.keys(context).forEach(key => {
axios[key] = context[key]
})
// 拦截器管理器构造函数
function InterceptorManager(){
this.handlers = []
}
InterceptorManager.prototype.use = function(fulfilled, rejected) {
this.handlers.push({
fulfilled,
rejected
})
}
// 设置请求拦截器
axios.interceptors.request.use(function one(config){
console.log('请求拦截器一号 成功')
return config
}, function (error) {
console.log('请求拦截器一号 失败')
return Promise.reject(error)
})
// 设置请求拦截器
axios.interceptors.request.use(function two(config){
console.log('请求拦截器二号 成功')
return config
}, function (error) {
console.log('请求拦截器二号 失败')
return Promise.reject(error)
})
// 设置响应拦截器
axios.interceptors.response.use(function one(response){
console.log('响应拦截器一号 成功')
return response
}, function (error) {
console.log('响应拦截器一号 失败')
return Promise.reject(error)
})
// 设置响应拦截器
axios.interceptors.response.use(function two(response){
console.log('响应拦截器二号 成功')
return response
}, function (error) {
console.log('响应拦截器二号 失败')
return Promise.reject(error)
})
</script>
模拟实现axios取消请求
<script>
// 构造函数
function Axios(config) {
this.config = config
}
// 原型request方法
Axios.prototype.request = function (config) {
return dispatchRequest(config)
}
// dispatchRequest 函数
function dispatchRequest(config) {
return xhrAdapter(config)
}
// xhrAdapter
function xhrAdapter(config) {
// 发送ajax请求
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) {
// 设置成功的状态
resolve({
status: xhr.status,
statusText: xhr.statusText
})
} else {
reject(new Error('请求失败'))
}
}
}
// 关于取消请求的处理
if (config.cancelToken) {
// 对cancelToken对象身上的promise对象指定成功的回调
config.cancelToken.promise.then(value => {
xhr.abort()
reject('请求已经被取消')
})
}
})
}
// 创建axios函数
const context = new Axios({})
const axios = Axios.prototype.request.bind(context)
// celToken 构造函数
function CancelToken(executor) {
// 声明一个变量
var resolvePromise
// 为实例对象添加属性
this.promise = new Promise((resolve) => {
// 将resolve赋值给resolvePromise
resolvePromise = resolve
})
// 调用executor函数
executor(function () {
// 执行resolvePromise函数
resolvePromise()
})
}
//获取按钮
const btns = document.querySelectorAll('button')
// 2. 声明全局变量
let cancel = null
btns[0].onclick = function () {
// 检测上一侧的请求是否完成
if (cancel !== null) {
// 表明上一次的请求还在继续,没有完成,所以取消上一次的请求,只执行最后一次请求
cancel()
}
// 创建canceltoken的值
let cancelToken = new CancelToken(function (c) {
// 将c的值赋给cancel
cancel = c
})
axios({
method: 'GET',
url: 'http:localhost:3000/posts',
cancelToken: cancelToken
}).then(response => {
console.log(response)
cancel = null
})
}
btns[1].onclick = function () {
cancel()
}
</script>