Axios source code imitation and secondary packaging

Axios source code analysis

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

test data

copy source code

  • Define a createInstance to instantiate Axios and mount methods and properties on it
  • Call request to send request => call dispatchRequest to send request => call xhrAdapter to send request (the real network request is sent here)
  • Handling Interceptors
    • Push the request interceptor unshift into the chain array
    • Add the response interceptor push to the chain array
  • Define a CancelToken to cancel the network request by executing xhr.about() after a successful callback
<!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>

secondary packaging

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
// }

Guess you like

Origin blog.csdn.net/weixin_64925940/article/details/125832158