当前位置: 首页 > news >正文

Axios源码仿写与二次封装

Axios源码解析

https://wuwhs.gitee.io/demo/keyboard-compatible/input.html

测试用的数据

  • 通过json-server模拟后端数据发送

仿写源码

  • 定义一个createInstance用来实例化Axios并在上面挂载方法和属性
  • 调用request发送请求=>调用dispatchRequest发送请求=>调用xhrAdapter发送请求(真正在网络请求在此处发送)
  • 处理拦截器
    • 将请求拦截器unshift压入chain数组中
    • 将响应拦截器push添加到chain数组中
  • 定义一个CancelToken,通过成功回调后执行xhr.about()来取消网络请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button>发送请求</button>
    <br/>
    <button>取消请求</button>
    <script>
        // 声明构造函数
        function Axios(config){
            this.config=config
            this.interceptors={
                request:new InterceptorManager(),
                response:new InterceptorManager()
            }
        }
        // 拦截器管理器构造函数
        function InterceptorManager(){
            this.handlers=[]
        }
        InterceptorManager.prototype.use=function (fulfilled, rejected){
            this.handlers.push({
                fulfilled,
                rejected
            })
        }
        // 函数原型上挂载方法
        Axios.prototype.request=function (config){
            // 发送请求
            // 创建一个Promise对象
            let promise=Promise.resolve(config)
            // 声明一个数组
            const chain=[dispatchRequest,undefined] // undefined作用是占位
            // 循环数组 promise.then表示成功,那么必会执行第一个函数
            // const result=promise.then(chain[0],chain[1])
            /**
             * 处理拦截器
             */
            // 请求拦截器
            this.interceptors.request.handlers.forEach(item=>{
                chain.unshift(item.fulfilled,item.rejected)
            })
            // 响应拦截器
            this.interceptors.response.handlers.forEach(item=>{
                chain.push(item.fulfilled,item.rejected)
            })
            // 遍历
            while (chain.length>0){
                promise=promise.then(chain.shift(),chain.shift())
            }
            return promise

        }
        Axios.prototype.get=function (config){
            return this.request({method: 'GET',url:config.url})
        }
        Axios.prototype.post=function (config){
            return this.request({method:'POST',url:config.url})
        }
        /**
         * dispatchRequest函数
         */
        function dispatchRequest(config){
            /**
             * 调用适配器发送请求
             */
            return xhrAdapter(config).then(res=>{
                // 对响应结果进行准换处理
                return res
            },error=>{
                throw error
            })
        }

        /**
         * 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('请求失败,状态码为'+xhr.status))
                        }
                    }

                }
                // 取消网络请求的处理
                if(config.cancelToken){
                    // 对cancelToken进行回调成功处理
                    config.cancelToken.promise.then(value => {
                        xhr.abort()
                        reject(new Error("请求取消"))
                    })
                }
            }))
        }
        // cancelToken构造函数
        function CancelToken(executor){
            // 声明一个变量
            let  resolvePromise
            // 为实例对象添加属性
            this.promise=new Promise(resolve=>{
                // 将resolve赋值给resolvePromise
                resolvePromise=resolve
            })
            executor(function (){
                // 执行resolvePromise函数
                resolvePromise()
            })

        }
        // 声明函数
        function createInstance(config){
            const context=new Axios(config) // 目前已经可以调方法 context.get()
            const instance=Axios.prototype.request.bind(context)
            /**
             * 将Axios原型上方法挂载到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
        }
        const axios=createInstance()
        /**
         * 测试
         */
        // 设置请求拦截器  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);
        });
        const btns=document.querySelectorAll('button')
        let cancel=null
        btns[0].onclick=function (){
            if(cancel!==null){
                // 取消还未完成的请求
                cancel()
            }
            // 创建cancelToken的值
            let cancelToken=new CancelToken(function (c){
                cancel=c
            })
            axios({
                method:'get',
                url:'http://localhost:3000/posts/1',
                cancelToken:cancelToken
            }).then(data=>{
                console.log(data)
                cancel=null
            })
        }
        // 取消请求
        btns[1].onclick=function (){
            cancel()
        }
        // axios({
        //     method:'get',
        //     url:'http://localhost:3000/posts/1'
        // }).then(data=>{
        //     console.log(data)
        // })
        // axios.get({url:'http://localhost:3000/posts/1'}).then(data=>{
        //     console.log(data)
        // })
    </script>
</body>
</html>

二次封装

import axios from 'axios';
import qs from 'qs';
/**
 * 判断是什么环境
 */
switch (process.env.NODE_ENV) {
  // 生产环境
  case 'production':
    axios.defaults.baseURL = 'http://127.0.0.1';
    break;
  // 测试环境
  case 'test':
    axios.defaults.baseURL = 'http://127.0.0.2';
    break;
  // 默认未开发环境
  default:
    axios.defaults.baseURL = 'http://localhost:3000';
}
/**
 * 设置超时时间
 * 设置跨域是否携带凭证
 */
axios.defaults.timeout = 1000;
axios.defaults.withCredentials = true;
/**
 * 设置请求头(可以根据实际更改)
 * x-www-form-urlencoded // xxx=xxx&xxx=xxx
 */
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = (data) => qs.stringify(data);
/**
 * 设置拦截器
 */
axios.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  token && (config.headers.Authorization = token);
  return config;
}, (error) => Promise.reject(error));
/**
 * 响应拦截器
 */
// axios.defaults.validateStatus = (status) => {
//   // 自定义成功状态码
//   /^(2|3)\d{2}$/.test(status);
// };
axios.interceptors.response.use((response) => response.data, (error) => {
  const { response } = error;
  if (response) {
    // 服务器有结果返回
    switch (response.status) {
      case '401': // 需要验证
        break;
      case '403': // 服务器拒绝执行,一般token过期
        break;
      case '404': // 找不到地址
        break;
      default:
    }
  } else {
    if (!window.navigator.onLine) {
      // 断网处理
      return Promise.reject(new Error('没网了'));
    }
    return Promise.reject(error);
  }
});
export default axios;
// 使用
// import axios from './request.js'
// const login=()=>{
//   return axios.post('/login',{
//     xxx:'xxx'
//   })
// }
// export default {
//   login
// }

相关文章:

  • PHP学习笔记(才贯二酉)
  • 基于Springboot+vue的电影院管理系统(Java毕业设计)
  • 沉睡者IT - 闪剪AI数字人助你快速占领流量市
  • 4个Python推导式相关的开发技巧
  • 计算机网络——媒体接入控制
  • 一起来学反射
  • 一文了解Java序列化与反序列化
  • 【.Net实用方法总结】 整理并总结System.Data中DataColumn类及其方法介绍
  • ffmpeg、ffplay、ffprobe 常用命令详解(音视频必备)
  • [SpringMVC] SpringMVC入门
  • [ 数据结构 - C++]红黑树RBTree
  • 【闲笔杂谈】ArrayList的构造与扩容机制
  • Flink系列之:基于Flink CDC2.0实现海量数据的实时同步和转换
  • [架构之路-21]:目标系统 - 软件系统 - 计算机系统架构、计算机指令系统、结构化程序与分层编程。
  • acwing算法基础模版分析
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 2018一半小结一波
  • ES6 ...操作符
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • Java 内存分配及垃圾回收机制初探
  • javascript面向对象之创建对象
  • Java教程_软件开发基础
  • rabbitmq延迟消息示例
  • underscore源码剖析之整体架构
  • 第十八天-企业应用架构模式-基本模式
  • 服务器之间,相同帐号,实现免密钥登录
  • 给第三方使用接口的 URL 签名实现
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 设计模式走一遍---观察者模式
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 你对linux中grep命令知道多少?
  • 阿里云服务器如何修改远程端口?
  • $(function(){})与(function($){....})(jQuery)的区别
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (C语言)逆序输出字符串
  • (TOJ2804)Even? Odd?
  • (分布式缓存)Redis分片集群
  • (附源码)php新闻发布平台 毕业设计 141646
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (实战篇)如何缓存数据
  • (一)Linux+Windows下安装ffmpeg
  • (原創) 物件導向與老子思想 (OO)
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)shell调试方法
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .net core 控制台应用程序读取配置文件app.config
  • .net 反编译_.net反编译的相关问题
  • .net/c# memcached 获取所有缓存键(keys)
  • .net6 webapi log4net完整配置使用流程
  • @AutoConfigurationPackage的使用
  • @Bean, @Component, @Configuration简析
  • [ C++ ] STL---stack与queue
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [android] 天气app布局练习
  • [Angular] 笔记 16:模板驱动表单 - 选择框与选项