axios源码分析与模拟(上)

axios源码分析与模拟(上)

axios对象创建过程模拟实现

js 复制代码
//构造函数
        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 实例。

  1. Axios 构造函数
js 复制代码
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 属性,用于存储请求拦截器和响应拦截器。

  1. Axios 原型方法
js 复制代码
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'。

  1. createInstance 函数
js 复制代码
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。

  1. 使用 createInstance 函数创建 axios 实例
js 复制代码
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请求发送功能

js 复制代码
//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);
        })
  1. Axios 构造函数
js 复制代码
function Axios(config){
    this.config = config;
}

Axios 是一个构造函数,它接受一个 config 对象并将其赋值给实例的 config 属性。

  1. Axios.prototype.request 方法
js 复制代码
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。

  1. dispatchRequest 函数
js 复制代码
function dispatchRequest(config){
    // 调用适配器发送请求
    return xhrAdapter(config).then(res => {
        // 响应的结果进行转换处理
        // ...
        return res;
    }, error => {
        throw error;
    });
}

dispatchRequest 函数接收 config 对象,并调用 xhrAdapter 发送请求。

处理请求成功和失败的结果,返回一个 Promise。

  1. xhrAdapter 函数
js 复制代码
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,传入错误信息。

  1. 创建 axios 函数
js 复制代码
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拦截器的实现

js 复制代码
//构造函数
        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 库,带有请求和响应拦截器功能。下面是对代码的详细解释:

  1. Axios 构造函数
js 复制代码
function Axios(config) {
    this.config = config;
    this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager(),
    };
}

Axios 构造函数初始化了一个 config 配置对象,并创建了 interceptors 对象,其中包含 request 和 response 两个拦截器管理器。

  1. Axios.prototype.request 方法
js 复制代码
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 对象。

  1. dispatchRequest 函数
js 复制代码
function dispatchRequest(config) {
    return new Promise((resolve, reject) => {
        resolve({
            status: 200,
            statusText: 'OK',
        });
    });
}

dispatchRequest 函数模拟发送请求,直接返回一个 resolved 的 Promise 对象,表示请求成功。

  1. 创建 Axios 实例
js 复制代码
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 函数对象上。

  1. InterceptorManager 构造函数和 use 方法
js 复制代码
function InterceptorManager() {
    this.handlers = [];
}
InterceptorManager.prototype.use = function(fulfilled, rejected) {
    this.handlers.push({ fulfilled, rejected });
}

InterceptorManager 用于管理拦截器。

use 方法用于添加拦截器,handlers 数组存放拦截器的处理函数。

  1. 添加拦截器
js 复制代码
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);
});

代码添加了多个请求和响应拦截器。

每个拦截器都有成功和失败的回调函数。

  1. 发送请求
js 复制代码
axios({
    method: 'GET',
    url: 'http://localhost:3000/posts',
}).then(res => {
    console.log(res);
});

最后使用 axios 函数发送一个 GET 请求,并处理响应结果。

相关推荐
程序员爱技术1 小时前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
衣乌安、2 小时前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜2 小时前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
龙鸣丿2 小时前
Linux基础学习笔记
linux·笔记·学习
低代码布道师2 小时前
CSS的三个重点
前端·css
耶啵奶膘3 小时前
uniapp-是否删除
linux·前端·uni-app
Nu11PointerException4 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
王哈哈^_^5 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
亦枫Leonlew5 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法