axios实现

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 $timeoutms exceed`);
        ;
      

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


重难点

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

以上是关于axios实现的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Node、Express、Axios 在 ReactJS 前端设置带有 JWT 令牌的 cookie

NuxtJs 如何使用 axios filter api?

我如何在 Axios 中传递 cookie

模拟axios的创建[ 实现调用axios()自身发送请求或调用属性的方法发送请求axios.request() ]

axios学习笔记

细谈 axios和ajax区别