axios实现

类型定义

  import { AxopsInterceptroManager } from './AxiosINterceptoManager'
  interface AxiosInstace<T = any> {
    // request方法
    (config: AxiosRequestConfig):Promise<AxiosResponse<T>> //T是resolve(v)的v值的定义,
    interceptors: {
        request: AxopsInterceptroManager<AxiosRequestConfig>,
        response: AxopsInterceptroManager<AxiosResponse<T>>
    }
}






interface AxiosRequestConfig {
    url?: string;
    method?: Methods;
    params?: Record<string, any>;
    headers?: Record<string, any>
    data?: Record<string, any>
    timeout?: number
}

type Methods = 'get'|'GET'|'post'|'POST'|'put'|'PUT'|'delete'|'DELETE'


// 定义axios返回的类型
interface AxiosResponse<T=any>{
data:T,
status: number
statusText: string
headers: Record<string, any>
request: XMLHttpRequest
config?:AxiosRequestConfig
}


export type { AxiosInstace, AxiosResponse, AxiosRequestConfig }

index.ts

import { Axios } from './axios'
import { AxiosInstace, AxiosResponse } from './types'

function createInstance(){
   let context: Axios = new Axios() 
   // 让request里面的方法永远指向context
   let instance: AxiosInstace =  Axios.prototype.request.bind(context)
   // 把Axios的类的实例和类的原型上拷贝到request方法上。。
   instance = Object.assign(instance, Axios.prototype, context)

   return instance
}

let axios = createInstance()
export default axios
axios({url: '',params: {}, method: 'GET'}).then((res: AxiosResponse)=>{
   console.log(res.data);
}).catch(err=>{})

//拦截器的基类


interface OnFulfilled<T>{
    (v:T):T|Promise<T>
}
interface OnReject{
    (error: any):any
}


// 存放着每次use的值
export interface Interceptor<T> {
    onFulfilled?: OnFulfilled<T>
    onRejected?: OnReject
}

interface AxiosInterceptorManager<T>{
    use(onFuilled?:OnFulfilled<T>, onRejected?: OnReject):number
    eject(id: number): void
}


// 类 interceptors.request/response的实例
export class AxopsInterceptroManager<T> implements AxiosInterceptorManager<T> {
    public interceptors: Array<Interceptor<T> | null> = []
    // 每次use,就将两个方法存入数组
    use(onFulfilled: OnFulfilled<T> = v=>v, onRejected: OnReject = r=>r): number {
        this.interceptors.push({
            onFulfilled,
            onRejected
        })
        return this.interceptors.length -1 // 返回索引
    }
    eject(id: number): void {
        if(this.interceptors[id]){
            this.interceptors[id] = null
        }
    }
}

主要逻辑

import * as qs from "qs";
import { AxopsInterceptroManager, Interceptor } from "./AxiosINterceptoManager";
import { AxiosInstace, AxiosRequestConfig, AxiosResponse } from "./types";

// T是响应数据
export class Axios<T = any> {
  // 拦截器
  public interceptors = {
    //  实例,里面有数组,存放着所有的请求篮球机器
    request: new AxopsInterceptroManager<AxiosRequestConfig>(),
    reponse: new AxopsInterceptroManager<AxiosResponse<T>>(),
  };

  // 限制response的data的类型
  request(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
    const chain: Interceptor<AxiosRequestConfig | AxiosResponse<T>>[] = [
      // 真正的请求
      {
        onFulfilled: this.dispatchRequest,
        onRejected: (error) => error,
      },
    ];

    // 获取所有注册的请求拦截器
    this.interceptors.request.interceptors.forEach(
      (interceptor: Interceptor<AxiosRequestConfig> | null) => {
        // 后注册的放入队头
        interceptor && chain.unshift(interceptor);
      }
    );

    // 获取所有注册的响应拦截器
    this.interceptors.reponse.interceptors.forEach(
      (interceptor: Interceptor<AxiosResponse<T>> | null) => {
        // 后注册的放入对尾
        interceptor && chain.push(interceptor);
      }
    );

    let promise:Promise<AxiosRequestConfig | AxiosResponse<T>>  = Promise.resolve(config)

    // 执行拦截器和真正的请求
      while(chain.length){
        const {onFulfilled, onRejected} = chain.shift()! //取出第一个执行
        // 将config传入并且改造,然后他会继续返回,下一次Promise.then的时候就是新的config了
        // 到了真正的请求,返回的值就是resolve(data)了,就是返回的data,然后继续将返回的data往下传递。
        promise = promise.then(onFulfilled, onRejected)
      }
     return promise
  }

  // 派发请求
  dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return new Promise((resolve, reject) => {
      let request = new XMLHttpRequest();
      let query: string = "";
      let { method = "get", url = "", params, data, headers, timeout } = config;

      // 处理headers
      if (params && typeof params === "object") {
        // {a:1, b:2} => a=1&b=2
        query = qs.stringify(params);
      }
      request.open(`${method}${query ? "?" + query : ""}`, url, true);
      request.responseType = "json";

      // 处理响应体
      request.onreadystatechange = function () {
        if (
          request.readyState &&
          request.status >= 200 &&
          request.status <= 300
        ) {
          // 响应拦截器

          const response: AxiosResponse<T> = {
            data: request.response ? request.response : request.responseText,
            status: request.status,
            statusText: request.statusText,
            config,
            headers: request.getAllResponseHeaders, // 响应头
            request,
          };
          resolve(response);
        } else {
          // 状态码错误
          reject(`Error: Request failed with status code ${request.status}`);
        }
      };

      // 处理headers
      if (headers) {
        for (let key in headers) {
          request.setRequestHeader(key, headers[key]);
        }
      }

      // 处理post的body
      let body: string | null = null;
      if (data) {
        body = JSON.stringify(data);
      }

      //错误处理

      // 网络错误
      request.onerror = function () {
        reject("net:: ERR_INTERNET_DISCONNECTED");
      };
      // 超时错误
      if (timeout) {
        request.timeout = timeout;
        request.ontimeout = function () {
          reject(`Error: timeout of ${timeout}ms exceed`);
        };
      }

      // 请求拦截器
      request.send(body);
    });
  }
}

重难点
  • 将所有的请求拦截+真正发请求的promise+响应拦截放入一个数组。
  • 遍历使用promise.then去执行,一开始的config一直传
  • 传到发送请求的promise后,就变成了data,然后data作为res继续传给响应拦截器。
  • 简单的axios就完成了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值