Axios源码分析:
① 模拟Axios对象的创建过程:
1. Axios构造函数本身应具有defaults(默认配置参数)和intercepters(拦截器参数)
2. 在Axios原型上添加request、get、post等方法。
3. 创建实例化函数:实例化axios对象,创建instance对象并赋值axios原型上的方法(使用bind方法将this指向指向实例化的axios对象)使instance成为函数对象。
4. 遍历Axios原型的方法,给instance对象添加方法,使得其可直接使用方法或调用使用。
5. 遍历axios的实例化对象,为instance对象添加defaults和intercepters属性。
// 构造函数 function Axios(config) { this.defaults = config 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) // 创建请求函数 let instance = Axios.prototype.request.bind(context) // instance成为函数 不能再使用instance.get // 将Axios.prototype中的方法添加到instance中 Object.keys(Axios.prototype).forEach(key => { instance[key] = Axios.prototype[key].bind(context) }) // 为instance添加属性defaults和intercepters Object.keys(context).forEach(key => { instance[key] = context[key] }) return instance }
② 模拟Axios对象的请求过程:
1. 声明构造函数Axios。
2. Axios原型添加request方法,在其中创建promise对象和chains数组,并使用then方法指定回调,使整个方法返回promise对象。
3. 声明dispatchRequest方法,负责发送请求,其中调用适配器函数并使用then方法回调promise结果。
4. 声明适配器adapter,其中返回的是一个promise对象。在其中使用xhr发起Ajax请求,在成功处进行resolve进行数据处理。
// 1.声明构造函数 function Axios(config) { this.config = config } Axios.prototype.request = function (config) { // 发送请求 // 创建 promise let promise = Promise.resolve(config) // 创建数组,undefined实现占位 let chains = [dispatchRequest, undefined] // 调用 then方法指定回调 let result = promise.then(chains[0], chains[1]) // 返回 promise结果 return result } // 2.dispatchRequest function dispatchRequest(config) { // 调用适配器发送请求 return xhrAdapter(config).then(response => { // 响应结果转化处理 return response }, error => { throw error }) } // 3.adapter适配器 function xhrAdapter(config) { return new Promise((resolve, reject) => { 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))
③ 模拟Axios拦截器:
1. 创建构造函数Axios,属性为config配置对象和interceptors(对象属性为request和response,均为InterceptorManager的实例)
2. 创建拦截器管理构造函数InterceptorManager:添加handlers数组属性,存储拦截器的回调。
3. 为InterceptorManager原型添加use方法:参数为promise的状态,直接压入handlers中。
4. 为Axios原型添加request方法:在其中定义各种请求操作,定义promise对象,并定义调用方法的chains数组。设置处理拦截器的方法:对于请求拦截器,采取将handlers内的拦截器回调使用unshift压入chains数组最前方。对于响应拦截器,采取将handlers内的拦截器回调使用push方法压入chains数组最后。最后遍历chains数组,从首位向后依次遍历,不断生成promise对象。
5. 定义dispatchRequest函数:实现ajax请求。
// 构造函数 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) }) // 响应拦截器:将响应拦截器的回调 压入chains末尾 this.interceptors.response.handlers.forEach(item => { chains.push(item.fulfilled, item.rejected) }) // 遍历chains,不断调用then方法从chains数组中取出并生成promise while (chains.length) { promise = promise.then(chains.shift(), chains.shift()) } return promise } function dispatchRequest(config) { // 返回一个promise队形 return new Promise((resolve, reject) => { resolve({ status: 200, statusText: 'OK' }) }) } // 拦截器管理器构造函数 function InterceptorManager() { this.handlers = [] } InterceptorManager.prototype.use = function (fulfilled, rejected) { this.handlers.push({ fulfilled, rejected }) } // 创建axios函数 // 创建实例 将context 属性 config和 interceptors添加至axios let context = new Axios({}) let axios = Axios.prototype.request.bind(context) Object.keys(context).forEach(key => { axios[key] = context[key] }) axios.interceptors.request.use(config => { console.log('请求拦截器1'); return config }, error => { return Promise.reject(error) }) axios.interceptors.request.use(config => { console.log('请求拦截器2'); return config }, error => { return Promise.reject(error) }) axios.interceptors.response.use(config => { console.log('响应拦截器1'); return config }, error => { return Promise.reject(error) }) axios.interceptors.response.use(config => { console.log('响应拦截器2'); return config }, error => { return Promise.reject(error) }) axios({ method: 'GET', url: 'http://localhost:3000/posts' }).then(response => console.log(response))
注意:由于对于请求拦截器回调和响应拦截器回调的压入方式不同,故造成了不同的执行时机,最后的执行结果为:请求拦截器2、请求拦截器1、响应拦截器1、响应拦截器2。
这里的undefined实际上起到了占位的作用,使得整个执行链能正常运行。当拦截器均为成功时,像跳板一样不断执行至最后。
当执行失败时,走到undefined由于错误穿透缘故不会立即终止而是继续执行至最后。
标签:拦截器,return,chains,前端,Axios,promise,Day45,config From: https://www.cnblogs.com/LWHCoding/p/16768239.html