axios源码分析与模拟(上)
axios对象创建过程模拟实现
//构造函数 function Axios(config){ //初始化 this.defaults=config;//为了创建default默认属性 this.intercepers={ 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() context.post() 但是不能当作函数使用 context() //创建请求函数 let instance=Axios.prototype.request.bind(context);//instance是一个函数,并且可以instance({}) 此时不能instance.get() //将Axios.prototype 对象中的方法添加大instance函数对象中 Object.keys(Axios.prototype).forEach(key=>{ instance[key]=Axios.prototype[key].bind(context);//this.default this.interceptors }); console.dir(instance); //为instance函数对象添加属性 default与interceptors Object.keys(context).forEach(key=>{ console.log(key); instance[key]=context[key]; }); return instance; } let axios=createInstance({method:'GET'}); //发送请求 axios既可以做函数也可以做对象 axios({method:'GET'}); axios({method:'POST'}); axios.get({}); axios.post({});
这段代码模拟了 Axios 库的基本结构,并展示了如何使用 createInstance 函数来创建一个可用于发送请求的 Axios 实例。
- Axios 构造函数
function Axios(config){ // 初始化 this.defaults = config; // 用于创建默认配置 this.intercepers = { request: {}, response: {} }; }
Axios 构造函数:
function Axios(config) 定义了 Axios 构造函数,用于初始化 Axios 实例。
this.defaults = config; 将传入的配置 config 存储在 defaults 属性中,作为默认配置。
this.interceptors = { request: {}, response: {} }; 初始化 interceptors 属性,用于存储请求拦截器和响应拦截器。
- Axios 原型方法
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'}); }
request 方法接受一个 config 对象,并输出请求的类型。
get 方法调用 request 方法,并指定 method 为 ‘GET’。
post 方法调用 request 方法,并指定 method 为 ‘POST’。
- createInstance 函数
function createInstance(config) { // 实例化一个对象 let context = new Axios(config); // 创建请求函数 let instance = Axios.prototype.request.bind(context); // 将Axios.prototype对象中的方法添加到instance函数对象中 Object.keys(Axios.prototype).forEach(key => { instance[key] = Axios.prototype[key].bind(context); }); console.dir(instance); // 为instance函数对象添加属性 default与interceptors Object.keys(context).forEach(key => { console.log(key); instance[key] = context[key]; }); return instance; }
createInstance 函数接受一个 config 参数并创建一个 Axios 对象 context。
创建一个绑定到 context 的 request 方法,并命名为 instance。这样 instance 就是一个函数,可以用来发送请求。
将 Axios.prototype 上的所有方法绑定到 instance 上,这样 instance 也可以调用 get 和 post 方法。
将 context 的所有属性(如 defaults 和 intercepers)也添加到 instance 上。
最后返回 instance。
- 使用 createInstance 函数创建 axios 实例
let axios = createInstance({method: 'GET'}); // 发送请求 axios 既可以做函数也可以做对象 axios({method: 'GET'}); axios({method: 'POST'}); axios.get({}); axios.post({});
axios 是通过 createInstance 创建的实例。
axios 可以像函数一样调用,例如 axios({method: ‘GET’}),这将调用 request 方法并输出请求类型。
axios 也可以像对象一样调用 get 和 post 方法,这些方法同样会调用 request 方法并输出请求类型。
模拟实现axios请求发送功能
//axios发送请求 axios Axios.prototype.request bind //1.声明构造函数 function Axios(config){ this.config=config; } Axios.prototype.request=function(config){ //发送请求 //创建一个promise对象 let promise=Promise.resolve(config); //声明一个数组 let chains=[dispatchRequest,undefined];//undefined占位 //调用then方法指定回调 let result=promise.then(chains[0],chains[1]); /**等价于 * let result=promise.then(function dispatchRequest(config){ console.log('dispatchRequest函数'); },chains[1]); */ //返回promise的结果 return result; } //2.dispatchRequest函数 function dispatchRequest(config){ //调用适配器发送请求 return xhrAdapter(config).then(res=>{ //响应的结果进行转换处理 //... return res; },error=>{ throw error; }) } //3.adapter适配器 function xhrAdapter(config){ //发送AJAX请求 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(),//字符串 parseHeaders //xhr请求对象 request:xhr, //响应状态码 status:xhr.status, //相应状态字符串 statusText:xhr.statusText }); }else{ //失败的状态 reject(new Error('请求失败 失败的状态码为 '+xhr.status)); } } } }) } //4.创建axios函数 let axios=Axios.prototype.request.bind(null); axios({ method:'GET', url:'http://localhost:3000/posts', }).then(res=>{ console.log(res); })
- Axios 构造函数
function Axios(config){ this.config = config; }
Axios 是一个构造函数,它接受一个 config 对象并将其赋值给实例的 config 属性。
- Axios.prototype.request 方法
Axios.prototype.request = function(config){ // 创建一个promise对象 let promise = Promise.resolve(config); // 声明一个数组 let chains = [dispatchRequest, undefined]; // undefined 占位 // 调用then方法指定回调 let result = promise.then(chains[0], chains[1]); // 返回promise的结果 return result; }
request 方法接收一个 config 对象,并返回一个 Promise 对象。
它首先创建一个 resolved 状态的 Promise,传入 config 作为值。
声明一个包含 dispatchRequest 函数和 undefined 的数组 chains。
使用 then 方法为 Promise 指定成功和失败的回调函数,并返回处理后的 Promise。
- dispatchRequest 函数
function dispatchRequest(config){ // 调用适配器发送请求 return xhrAdapter(config).then(res => { // 响应的结果进行转换处理 // ... return res; }, error => { throw error; }); }
dispatchRequest 函数接收 config 对象,并调用 xhrAdapter 发送请求。
处理请求成功和失败的结果,返回一个 Promise。
- xhrAdapter 函数
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('请求失败 失败的状态码为 ' + xhr.status)); } } }; }); }
xhrAdapter 函数返回一个新的 Promise,用于发送 AJAX 请求。
创建并初始化 XMLHttpRequest 对象 xhr,设置请求方法和 URL。
绑定 onreadystatechange 事件处理函数,当请求完成时检查响应状态:
成功时调用 resolve,传入响应数据。
失败时调用 reject,传入错误信息。
- 创建 axios 函数
let axios = Axios.prototype.request.bind(null); axios({ method: 'GET', url: 'http://localhost:3000/posts', }).then(res => { console.log(res); });
axios 是通过绑定 Axios.prototype.request 方法创建的,这样 axios 函数就可以直接调用 request 方法。
调用 axios 函数发送一个 GET 请求到 http://localhost:3000/posts。
使用 then 方法处理响应结果,输出响应数据。
总结
这段代码实现了一个简化版的 axios,核心流程如下:
创建一个 Axios 实例。
通过 axios 函数调用 request 方法,返回一个 Promise。
request 方法调用 dispatchRequest 函数。
dispatchRequest 函数调用 xhrAdapter 函数,发送 AJAX 请求。
xhrAdapter 函数处理请求的结果,并返回一个 Promise。
axios 函数调用 then 方法处理最终的响应结果。
模拟axios拦截器的实现
//构造函数 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的前面 request.handles=[] this.interceptors.request.handlers.forEach(item=>{ chains.unshift(item.fulfilled,item.rejected); }) //响应拦截器 this.interceptors.response.handlers.forEach(item=>{ chains.push(item.fulfilled,item.rejected); }) //遍历 while(chains.length>0){ promise=promise.then(chains.shift(),chains.shift()) } //返回一个promise队形 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 }) } //以下为功能测试代码 //设置请求拦截器1 config 配置对象 axios.interceptors.request.use(function(config){ console.log("请求拦截器 success--1号"); //修改config中的参数 config.params={a:100}; return config; // throw "抛出失败,返回失败的promise" },function(error){ console.log("请求拦截器 fail--1号"); return Promise.reject(error); }) //设置请求拦截器2 axios.interceptors.request.use(function(config){ console.log("请求拦截器 success--2号"); //修改 config中的参数 config.timeout=2000; return config; // throw "抛出失败,返回失败的promise" },function(error){ console.log("请求拦截器 fail--2号"); return Promise.reject(error); }) //设置响应拦截器1 axios.interceptors.response.use(function(response){ console.log("响应拦截器 成功--1号"); return response.data; },function(error){ console.log("响应拦截器 失败--1号"); return Promise.reject(error); }) //设置响应拦截器2 axios.interceptors.response.use(function(response){ console.log("响应拦截器 成功--2号"); return response; },function(error){ console.log("响应拦截器 失败--2号"); return Promise.reject(error); }) console.dir(axios); axios({ method:'GET', url:'http://localhost:3000/posts', }).then(res=>{ console.log(res); })
这段代码实现了一个简化版的 Axios 库,带有请求和响应拦截器功能。下面是对代码的详细解释:
- Axios 构造函数
function Axios(config) { this.config = config; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager(), }; }
Axios 构造函数初始化了一个 config 配置对象,并创建了 interceptors 对象,其中包含 request 和 response 两个拦截器管理器。
- Axios.prototype.request 方法
Axios.prototype.request = function(config) { let promise = Promise.resolve(config); const chains = [dispatchRequest, undefined]; this.interceptors.request.handlers.forEach(item => { chains.unshift(item.fulfilled, item.rejected); }); this.interceptors.response.handlers.forEach(item => { chains.push(item.fulfilled, item.rejected); }); while (chains.length > 0) { promise = promise.then(chains.shift(), chains.shift()); } return promise; };
request 方法用于发送请求。
promise 先初始化为传入的 config 对象。
chains 数组用于存放拦截器的处理函数。
将请求拦截器的处理函数添加到 chains 的前面,将响应拦截器的处理函数添加到 chains 的后面。
遍历 chains 数组,并依次调用 then 方法,形成拦截器链。
返回最终的 promise 对象。
- dispatchRequest 函数
function dispatchRequest(config) { return new Promise((resolve, reject) => { resolve({ status: 200, statusText: 'OK', }); }); }
dispatchRequest 函数模拟发送请求,直接返回一个 resolved 的 Promise 对象,表示请求成功。
- 创建 Axios 实例
let context = new Axios({}); let axios = Axios.prototype.request.bind(context); Object.keys(context).forEach(key => { axios[key] = context[key]; });
创建一个 Axios 实例 context。
使用 bind 方法将 request 方法绑定到 context,形成 axios 函数。
将 context 的属性(如 config 和 interceptors)复制到 axios 函数对象上。
- InterceptorManager 构造函数和 use 方法
function InterceptorManager() { this.handlers = []; } InterceptorManager.prototype.use = function(fulfilled, rejected) { this.handlers.push({ fulfilled, rejected }); }
InterceptorManager 用于管理拦截器。
use 方法用于添加拦截器,handlers 数组存放拦截器的处理函数。
- 添加拦截器
axios.interceptors.request.use(function(config) { console.log("请求拦截器 success--1号"); config.params = { a: 100 }; return config; }, function(error) { console.log("请求拦截器 fail--1号"); return Promise.reject(error); }); axios.interceptors.request.use(function(config) { console.log("请求拦截器 success--2号"); config.timeout = 2000; return config; }, function(error) { console.log("请求拦截器 fail--2号"); return Promise.reject(error); }); axios.interceptors.response.use(function(response) { console.log("响应拦截器 成功--1号"); return response.data; }, function(error) { console.log("响应拦截器 失败--1号"); return Promise.reject(error); }); axios.interceptors.response.use(function(response) { console.log("响应拦截器 成功--2号"); return response; }, function(error) { console.log("响应拦截器 失败--2号"); return Promise.reject(error); });
代码添加了多个请求和响应拦截器。
每个拦截器都有成功和失败的回调函数。
- 发送请求
axios({ method: 'GET', url: 'http://localhost:3000/posts', }).then(res => { console.log(res); });
最后使用 axios 函数发送一个 GET 请求,并处理响应结果。