axios源码模拟实现

模拟实现axios对象创建过程


构造函数Axios,在Axios原型上添加相关的方法,声明一个createInstance()函数,在函数内部实例化一个对象context和创建一个请求对象函数instance,遍历将Axios.prototype对象中的方法添加到instance函数对象中,然后遍历将defaults与intercepters属性添加到instance上,然后返回instance。最后通过new createInstance()来实例化构造axios。

 <script>
        // 构造函数
        function Axios(config){
            // 初始化
            this.defaults = config;//为了创建default默认属性
            this.intercepters = {
                request:{},
                Response:{}
            }
        }
        // 原型添加相关的方法
        Axios.prototype.request = function(config){
            console.log('发送AJAX请求 请求类型为' + config.method);
        }
        //能发送请求的原理是因为Axios内部调用了request方法
        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不能instance.get
            // 将Axios.prototype对象中的方法添加到instance函数对象中
            Object.keys(Axios.prototype).forEach(key => {
                instance[key] = Axios.prototype[key].bind(context);
            });
            // 为instance函数对象添加属性 default 与 interceptors
            Object.keys(context).forEach(key => {
                instance[key] = context[key];
            });
            return instance;
        }
        let axios = createInstance();
        // 发送请求
        // axios({method: 'GET'});
        axios.get({});
        axios.post({});
    </script>

模拟实现axios发送请求过程 


  <script>
        // 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]);
            // 返回promise的结果
            return result;
        }
        // 2.dispatchRequest函数
        function dispatchRequest(config){
            // 调用适配器发送请求
            return xhrAdapter(config).then(response => {
                //响应的结果进行转换处理
                return response;
            }, error => {
                throw error;
            })
        }
        // 3.adapter 适配器
        function xhrAdapter(config){
            console.log('xhrAdapter 函数执行');
            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));
                        }
                    }
                }
            })
        }
        // 4.创建axios函数
        let axios = Axios.prototype.request.bind(null);
        axios({
            method:'GET',
            url:'http://localhost:3000/posts'
        }).then(response => {
            console.log(response);
        });
    </script>

模拟实现axios拦截器功能


请求拦截器:

在真正发送请求前执行的回调函数

可以对请求进行检查、配置或者特定处理

成功的回调函数:传递默认的是config(必须)

失败的回调函数:传递的默认是error 

响应拦截器:

在请求得到响应后执行的回调函数

可以对响应数据进行特定处理

成功的回调函数:默认传递的是response

失败的回调函数:默认传递的是error 

    <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前面 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());
            }
            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
            })
        }

        // 以下为功能测试代码
        // 设置请求拦截器  config 配置对象
        axios.interceptors.request.use(function one(config) {
            console.log('请求拦截器 成功 - 1');
            return config;
        }, function one(error) {
            console.log('请求拦截器 失败 - 1')
            return Promise.reject(error);
        });
        axios.interceptors.request.use(function two(config) {
            console.log('请求拦截器 成功 - 2');
            return config;
        }, function two(error) {
            console.log('请求拦截器 失败 - 2')
            return Promise.reject(error);
        });

        // 设置响应拦截器
        axios.interceptors.response.use(function (response) {
            console.log('响应拦截器 成功 - 1')
            return response;
        }, 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(response => {
            console.log(response);
        })
    </script>

模拟实现axios取消请求功能


在CancelToken身上维护了一个属性promise,然后将改变promise状态的变量resolvePromise暴露到全局中,cancel便成为了“钥匙”,只要cancel()一调用,内部的resolvePromise便执行, this.promise状态便变为成功,然后发送请求中的回调便执行,进而xhr.abort()。

<body>
    <div class="container">
        <h2 class="page-header">axios取消请求</h2>
        <button class="btn btn-primary">发送请求</button>
        <button class="btn btn-warning">取消请求</button>
    </div>
    <script>
        // 构造函数
        function Axios(config){
            this.config = config;
        }
        // 原型request方法
        Axios.prototype.request = function(config){
            return dispatchRequest(config);
        }
        //dispatchRequest 函数
        function dispatchRequest(config){
            return xhrAdapter(config);
        }
        // xhrAdapt 函数
        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( new Error('请求已经被取消'))
                    })
                }
            })
        }
        //创建axios函数
        const context = new Axios({});
        const axios = Axios.prototype.request.bind(context);
        //CancelToken构造函数
        function CancelToken(executor){
            //声明一个变量
            var resolvePromise;
            //为实例对象添加属性
            this.promise = new Promise((resolve) => {
                resolvePromise = resolve;
            });
            //调用executor函数
            executor(function() {
                //执行resolvePromise函数
                resolvePromise();
            })
        }
        //获取按钮 以上为模拟实现的代码
        const btns = document.querySelectorAll('button');
        let cancel = null;
        //发送请求
        btns[0].onclick = function() {
            // 检测上一次请求是否已经完成
            if(cancel !== null){
                // 取消上一次请求
                cancel();
            }
            //创建cancelToken的值
            var cancelToken = new CancelToken(function(c){
                    cancel = c;
                });
            axios({
                method: 'GET',
                url: 'http://localhost:3000/posts',
                // 1.添加配置对象的属性
                cancelToken: cancelToken
            }).then(response => {
                console.log(response);
                // 将cancel的值初始化
                cancel = null;
            })
        }
        //取消请求
        btns[1].onclick = function(){
            cancel();
        }
    </script>

axios与Axios的关系  


  • 从语法上来说:axios不是Axios的实例

axios是通过createInstance()创建来的,先造一个实例对象context,再造一个函数对象instance,把实例对象context原型上的方法放到instance函数身上,再把实例对象的属性加到Instance函数对象身上,最终返回instance,然后实例化createInstance形成axios。

function createInstance(defaultConfig) {
    //创建一个实例对象 context 可以调用 get  post put delete request
    var context = new Axios(defaultConfig);// context 不能当函数使用  
    // 将 request 方法的 this 指向 context 并返回新函数  instance 可以用作函数使用, 且返回的是一个 promise 对象
    var instance = bind(Axios.prototype.request, context);// instance 与 Axios.prototype.request 代码一致
    // instance({method:'get'});  instance.get() .post()
    // Copy axios.prototype to instance
    // 将 Axios.prototype 和实例对象的方法都添加到 instance 函数身上
    utils.extend(instance, Axios.prototype, context);// instance.get instance.post ...
    // instance()  instance.get()
    // 将实例对象的方法和属性扩展到 instance 函数身上
    utils.extend(instance, context);

    return instance;
}
// axios.interceptors

// Create the default instance to be exported
// 通过配置创建 axios 函数
var axios = createInstance(defaults);
  • 从功能上来说:axios是Axios的实例

axios拥有Axios实例对象上的方法,get、post等(通过扩展的方式把原型上的方法加到函数对象instance身上)。

  • axios是Axios.prototype.request函数bind()返回的函数

  • axios作为对象有Axios原型对象上的所有方法,有Axios对象上的所有属性

 instance与axios的区别


相同点

(1)都是一个能够法任意请求的函数:request(config)

(2)都有特定请求的各种方法:get()/post()/put()/delete()

(3)都有默认配置和拦截器的属性:defaults/interceptors

不同点:

(1)默认配置很可能不一样

(2)instance没有axios后面添加的一些方法:create()/CancelToken()/all()

// 通过配置创建 axios 函数
var axios = createInstance(defaults);

// Expose Axios class to allow class inheritance
// axios 添加 Axios 属性, 属性值为构造函数对象  axios.CancelToken = CancelToken    new axios.Axios();
axios.Axios = Axios;

// Factory for creating new instances
// 工厂函数  用来返回创建实例对象的函数
axios.create = function create(instanceConfig) {
    return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');

// Expose all/spread
axios.all = function all(promises) {
    return Promise.all(promises);
};
axios.spread = require('./helpers/spread');

module.exports = axios;

//简单实现全局暴露 axios
window.axios = axios;

// Allow use of default import syntax in TypeScript
module.exports.default = axios;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值