Ajax & Fetch API & axios 学习笔记

661 阅读13分钟

Ajax

开始

什么是Ajax

Asynchronous JavaScript And XML, 用来描述一种使用现有技术集合的 "新"方法, 包括 HTML / XHTML, CSS, JavaScript, DOM, XML, XSLT 以及最重要的 XMLHttpRequest.

XMLhttpRequest 用于于服务器通信, 它可以以多种格式发送和接受信息, 例如 JSON, XML, HTML, 文本文件.

使用结合了这些技术的 AJAX 模型后, 页面能够快速将增量更新呈现在用户界面而不需要重载整个页面.

尽管 X 在 AJAX 中代表 XML, 但是由于 JSON 的优势, JSON的使用比 XML 更加普遍.

Ajax 的两个重要特性为 :

  • 向服务器发出请求而无需重载页面
  • 接受并处理来自服务器的数据

第一步 : 如何发送 HTTP 请求

为了能够使用 JavaScript 发送 HTTP 请求, 我们需要一个拥有这种能力的对象, 这就是 XMLHttpRequest.

if (window.XMLHttpRequest) httpRequest = new XMLHttpRequest();
// IE 6 and older
else if (window.ActiveXObject) httpRequest = new AtiveXObject("Microsoft.XMLHTTP");

当发送请求后, 我们将接受到一个响应. 在这个阶段, 我们需要告诉请求对象哪个函数用于处理这个响应, 这可以通过设置请求的 onreadystatechange 处理器 :

httpRequest.onreadystatechange = function() {
    // ...
}

下一步, 我们需要确实发送一个请求, 这可以通过调用 opensend 方法 :

httpRequest.open('GET', 'http://www.example.org/some.file', true);
httpRequest.send();
  • open 的第一个参数是 HTTP 请求方法 —— GET, POST, HEAD, 或其他被我们的服务器支持的方法. 记住要保持方法大写, 否则有些浏览器不会处理请求.
  • 第二个参数为一个我们发送请求目的地的URL. 作为安全功能, 我们默认不能调用第三方的 domain. 确保在所有页面使用精确的域名, 否则我们当我们调用 open 时将得到 "permission denied" error. 一个常见的陷阱时通过 "domain.tld" 访问站点但却尝试使用 "www.domain.tld" 调用页面. 如果确实想要发送请求到别的域中, 查看HTTP access control (CORS) (Cross-Origin Resource Sharing)
  • 第三个参数设置请求是否为异步, 如果 true (默认), JavaScript 可以继续执行, 并且服务器响应尚未到达时用户可以与页面进行交互. 这是第一个 "Ajax" 的第一个 "A".

如果是 Post 请求的话, send 方法的参数可以是任何我们想发送到服务器的数据. 表单数据应该以服务器可以解析的格式发送, 如 query string, 或 multipart/form-data, JSON, XML 等等.

如果使用 POST 数据, 那就需要设置请求的 MIME 类型, 比如在调用 send 方法获取表单数据前要这样 :

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

第二步 : 处理服务器响应

发送请求时, onreadystatechange 负责处理响应

这个处理器需要干什么 ? 首先, 检查请求状态, 如果状态的值时 XMLHttpRequest.DONE (对应值 4), 意味着服务器响应收到了并且没问题, 然后就可以继续执行.

if (http.readyState === XMLHttpRequest.DONE) {
    // reponse received
}
else {
    // not ready yet
}

全部的 readyState

  • 0 (uninitialized未初始化) 或 (request not initialized请求未初始化)
  • 1 (loading加载中) 或 (server connection established已建立服务器连接)
  • 2 (loaded加载成功) 或 (request received请求已接受)
  • 3 (interative交互) 或 (processing request正在处理请求)
  • 4 (complete完成) 或 (request finished and response is ready请求已完成并且响应已准备好)

readyState

接下来, 判断 HTTP 响应码

if (httpRequest.status === 200) {
    // Perfect!
} else {
    // There was a problem with the request.
    // For example, the response may have a 404 (Not Found)
    // or 500 (Internal Server Error) response code.
}

检查完请求状态和 HTTP 响应码后, 就可以用服务器返回的数据做事情了. 可以用两个方法访问这些数据 :

  • httpRequest.responseText —— 服务器以文本字符的形式返回
  • httpRequest.responseXML —— 以 XMLDocument 对象形式返回, 之后可以使用 JS 处理

注意上面异步只在发送异步请求时有效 (即 open 第三参数为 true ). 同步请求不必使用函数, 但不推荐这样做, 用户体验会很差.

第三步 : 例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax Project</title>
</head>
<body>
    <button id="ajaxButton">Make a request</button>
    <script>
        (function() {
            let httpRequest;
            document.getElementById("ajaxButton").addEventListener("click", makeRequest);

            function makeRequest() {
                httpRequest = new XMLHttpRequest();
                if (!httpRequest) return false;

                httpRequest.onreadystatechange = alertContents;
                httpRequest.open("GET", 'test.html');
                httpRequest.send();
            }

            function alertContents() {
                try {
                    if (httpRequest.readyState === XMLHttpRequest.DONE) {
                        if (httpRequest.status === 200) {
                            alert(httpRequest.responseText);
                        }
                        else {
                            alert("There was a problem with the request");
                        }
                    }
                }
                catch (e) {
                    alert(e);
                }
            }
        })();
    </script>
</body>
</html>

注意 httpRequest 在全局范围使用的话会在 makeRequest 函数中被相互覆盖, 造成资源竞争. 为避免这个情况, 在包含 Ajax 函数的闭包中声明 httpRequest 变量

第四步 : 处理 XML 响应

上个例子使用了 responseText 属性, 我们还可以使用 responseXML :

<!-- test.xml -->
<?xml version="1.0" ?>
<root>
    I'm a test.
</root>
httpRequest.open('GET', "test.xml");
let xmldoc = httpRequest.responseXML;
let root = xmldoc.getElementByTagName('root').item(0);
alert(root.firstChild.data);

第五步 : 处理数据

document.getElementById("ajaxButton").onclick = function() { 
      var userName = document.getElementById("ajaxTextbox").value;
      makeRequest('test.php',userName); 
  };
function makeRequest(url, userName) {
    // ...

    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('POST', url);
    httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    httpRequest.send('userName=' + encodeURIComponent(userName));
  }
function alertContents() {
  if (httpRequest.readyState === XMLHttpRequest.DONE) {
    if (httpRequest.status === 200) {
      var response = JSON.parse(httpRequest.responseText);
      alert(response.computedString);
    } else {
      alert('There was a problem with the request.');
    }
  }
}

使用 XMLHttpRequest

请求类型

通过 XMLHttpRequest 生成的请求可以有两种方式来获取数据, 异步模式或同步模式. 请求的类型由 open 第三个参数 async 决定.

处理响应

W3C 规范规定了 XMLHttpRequest 对象的几种类型的响应属性, 这些属性告诉客户端关于 XMLHttpRequest 返回状态的重要信息.

  • 分析并操作 responseXML 属性
  • 解析和操作包含 HTML 文档的 responseText 属性

处理二进制数据

自从 responseType 支持大量附加内容类型后, 出现了很多现代技术使得发送和接收二进制数据更加容易.

例如 :

var xhr = new XMLHttpRequest();

xhr.onload = function(e) {
  var arraybuffer = xhr.response; // 不是 responseText !
  /* ... */
}
oReq.open("GET", url);
oReq.responseType = "arraybuffer";
oReq.send();

监测进度

XMLHttpRequest 提供了各种在请求被处理期间发生的事件以供监听. 这包括定期进度通知, 错误通知, 等等.

支持 DOM 的 progress 事件监测 XMLHttpRequest 传输, 遵循 Web API 进度事件规范 :

  • progress 检索的数据量发生变化
  • load 传输完成, 所有数据保存在 response
var oReq = new XMLHttpRequest();

oReq.addEventListener("progress", updateProgress);
oReq.addEventListener("load" , transferComplete);
oReq.addEventListener("error", transferFailed);
oReq.addEventListener("abort", transferCanceled);

oReq.open();

// ...

// 服务端到客户端的传输进程(下载)
function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total * 100;
    // ...
  } else {
    // 总大小未知时不能计算进程信息
  }
}

function transferComplete(evt) {
  console.log("The transfer is complete.");
}

function transferFailed(evt) {
  console.log("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  console.log("The transfer has been canceled by the user.");
}

注意要在请求调用 open 之前添加监听, 否则 progress 事件不触发.

如果使用 file: 协议, progress 事件无效.

使用 loadend 事件可以侦测到所有三种加载结束条件 (abort load error) :

req.addEventListener("loadend", loadEnd);

function loadEnd(e) {
  console.log("The transfer finished (although we don't know if it succeeded or not).");
} 

XMLHttpRequest

实例属性

XMLHttpRequest.prototype.onreadystatechange

XMLHttpRequest.prototype.readyState 只读

  • 0 请求未初始化
  • 1 服务器连接建立
  • 2 请求接收
  • 3 请求处理
  • 4 请求完成, 响应就绪

XMLHttpRequest.prototype.response 只读

XMLHttpRequest.prototype.responseType

XMLHttpRequest.prototype.responseURL 只读

XMLHttpRequest.prototype.responseText 只读

XMLHttpRequest.prototype.responseXML 只读

XMLHttpRequest.prototype.status 只读

  • 100 ~ 101 : 信息响应类, 表示接收到请求并继续处理
  • 200 ~ 206 : 成功响应类, 表示动作被成功接收
  • 300 ~ 308 : 重定向类, 为了完成指定动作, 必须接收进一步处理
  • 400 ~ 417 : 客户端错误类, 客户端请求包含语法错误或不能正确执行
  • 500 ~ 505 : 服务端错误类, 服务器不能正确执行一个正确的请求

XMLHttpRequest.prototype.statusText

XMLHttpRequest.prototype.timeout 设置延迟

XMLHttpRequest.prototype.upload 只读

方法

XMLHttpRequest.prototype.abort

XMLHttpRequest.prototype.open(method, url, async) 规定请求类型, URL 以及是否异步处理

XMLHttpRequest.prototype.send(string) 发送请求到服务器, 字符串仅用于 POST 请求

XMLHttpRequest.prototype.setRequestHeader

XMLHttpRequest.prototype.getResponseHeader

XMLHttpRequest.prototype.getAllResponseHeaders

XMLHttpRequest.prototype.overrideMimeType

事件

abort / onabort 属性

error / onerror

load / onload

loadend / onloadend

loadstart / onloadstart

progress / onprogress

timeout / ontimeout

提交表单和上传文件

XMLHttpRequest 实例有两种方式提交表单 :

  • 使用 Ajax
  • 使用 FormData API

第二种是最简单快捷的, 缺点是收集的数据无法使用 JSON.stringify 转化为一个 JSON 字符串

使用 Ajax 则更复杂, 但更加灵活强大

仅使用 XMLHttpRequest

大多数例子中, 提交表单时即便不使用 FormData API 也不会要求其他 API, 例外是要上传一个或多个文件, 需要额外的 FileReader API.

提交方法简介

一个 html <form> 可以用以下四种方式发送 :

  • 使用 POST 方法, 并将 enctype 属性设置为 application/x-www-form-urlencoded (默认)
  • 使用 POST 方法, 设置 enctypetext/pain
  • 使用 POST 方法, 设置 enctypemultipart/form-data
  • 使用 GET (enctype 被忽略 )

请求通常通过浏览器提交一个 <form> 自动完成. 如果想用 JS 做同样的事情, 那么不得不告诉解释器所有事情.

使用 FormData 对象

FormData 构造函数使我们编译一个键值对的集合, 然后使用 XMLHttpRequest 发送出去. 主要用于发送表格数据, 但也可以单独用来传输表格中用户指定的数据. 传输的数据格式与表格使用 submit 方法发送数据的格式一致

注意 FormData 对象不是课字符串化的对象

获取最后修改日期

function getHeaderTime () {
  var nLastVisit = parseFloat(window.localStorage.getItem('lm_' + this.filepath));
  var nLastModif = Date.parse(this.getResponseHeader("Last-Modified"));

  if (isNaN(nLastVisit) || nLastModif > nLastVisit) {
    window.localStorage.setItem('lm_' + this.filepath, Date.now());
    isFinite(nLastVisit) && this.callback(nLastModif, nLastVisit);
  }
}

function ifHasChanged(sURL, fCallback) {
  var oReq = new XMLHttpRequest();
  oReq.open("HEAD" /* 使用 HEAD - 我们仅需要头部信息(headers)! */, sURL);
  oReq.callback = fCallback;
  oReq.filepath = sURL;
  oReq.onload = getHeaderTime;
  oReq.send();
}

绕过缓存

一般, 如果缓存有相应内容, XMLHttpRequest 会试图在缓存中读取, 要绕过缓存需要 :

var req = new XMLHttpRequest();
req.open('GET', url, false);
req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
req.send(null);

或使用下面的方法自动更改缓存 :

var oReq = new XMLHttpRequest();

oReq.open("GET", url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime());
oReq.send(null);

Fetch

Fetch API 由这些部分组成

接口 :

  • Body
  • Headers
  • Request
  • Response

方法 :

  • WindowOrWorkerGlobalScope.fetch

fetch(input: RequestInfo, init?: RequestInit): Promise<Response>

  • 必须接收第一个参数作为请求信息
  • 第二个 init 为可选参数

Fetch 方法

fetch 规范和 jQuery.ajax() 主要有三种方式的不同

  • 当收到代表错误的 HTTP 状态码, 从 fetch 返回的 Promise 不会标记为 rejected, 即使是 404 / 500. 相反, 会标记为 fulfilled. 仅当网络故障或请求被阻止才标记为 rejected
  • fetch 可以接收跨域 cookies, 你也可以使用 fetch 建立起跨域会话
  • fetch 不会发送 cookies. 除非使用了 credentials 的初始选项

基本使用 :

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

fetch(input: RequestInfo, init: RequestInit): Promise<Response>

init 参数

  • method: 'GET' | 'POST' | ...
  • headers: { [field: string]: string } : 请求的头信息
  • body : 请求的 body 信息, 可能是一个 Blob, FormData, ...
  • mode: 'cors' | 'no-cors' | 'same-origin'
  • credentials: 'omit' | 'same-origin' | 'include' : 请求的 credentials, 为了在当前域名内自动发送 cookie, 必须提供这个选项
  • cache : 请求的 cache 模式, 如 default, no-store reload no-cache force-cache only-if-cached
  • redirect : follow 自动重定向, error 如果产生重定向将自动终止并抛出错误, 或者 manual 手动重定向
  • referrer : 一个 USVString 可以是 no-referrer client 或一个 URL, 默认 client
  • referrerPolicy : 指定了 HTTP 头部 referrer 字段的值. 可能为以下值之一 no-referrer no-rederrer-when-downgrade origin origin-when-cross-origin unsafe-url
  • integrity : 包括请求的 subresource integrity 值

发送带凭据的请求

为了让浏览器发送包含凭据的请求 (即使跨域源), 将 credentials: 'include' 添加到 init 对象中

fetch('https://example.com', {
  credentials: 'include'  
})

如果只想在请求 URL 与调用脚本位于同一起源处时发送凭据, 则添加 credentials: 'same-origin'

// The calling script is on the origin 'https://example.com'

fetch('https://example.com', {
  credentials: 'same-origin'  
})

确保浏览器不在请求中包含凭据, 使用 credentials: 'omit'

fetch('https://example.com', {
  credentials: 'omit'  
})

上传 JSON 数据

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

检查请求是否成功

fetch('flowers.jpg').then(function(response) {
  if(response.ok) {
    return response.blob();
  }
  throw new Error('Network response was not ok.');
}).then(function(myBlob) { 
  var objectURL = URL.createObjectURL(myBlob); 
  myImage.src = objectURL; 
}).catch(function(error) {
  console.log('There has been a problem with your fetch operation: ', error.message);
});

接口

// -----------------Body---------------------
interface Body {
    readonly body: ReadableStream<Uint8Array> | null;
    readonly bodyUsed: boolean;
    arrayBuffer(): Promise<ArrayBuffer>;
    blob(): Promise<Blob>;
    formData(): Promise<FormData>;
    json(): Promise<any>;
    text(): Promise<string>;
}

// -----------------Header---------------------
interface Headers {
    append(name: string, value: string): void;
    delete(name: string): void;
    get(name: string): string | null;
    has(name: string): boolean;
    set(name: string, value: string): void;
    forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
    [Symbol.iterator](): IterableIterator<[string, string]>;
    entries(): IterableIterator<[string, string]>;
    keys(): IterableIterator<string>;
    values(): IterableIterator<string>;
}

type HeadersInit = Headers | string[][] | Record<string, string>;

declare var Headers: {
    prototype: Headers;
    new(init?: HeadersInit): Headers;
};

// -----------------Request--------------------
type RequestCache = "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload";
type RequestCredentials = "include" | "omit" | "same-origin";
type RequestDestination = "" | "audio" | "audioworklet" | "document" | "embed" | "font" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt";
type RequestMode = "cors" | "navigate" | "no-cors" | "same-origin";
type RequestRedirect = "error" | "follow" | "manual";

interface Request extends Body {
    readonly cache: RequestCache;
    readonly credentials: RequestCredentials;
    readonly destination: RequestDestination;
    readonly headers: Headers;
    readonly integrity: string;
    readonly isHistoryNavigation: boolean;
    readonly isReloadNavigation: boolean;
    readonly keepalive: boolean;
    readonly method: string;
    readonly mode: RequestMode;
    readonly redirect: RequestRedirect;
    readonly referrer: string;
    readonly referrerPolicy: ReferrerPolicy;
    readonly signal: AbortSignal;
    readonly url: string;
    clone(): Request;
}

type RequestInfo = Request | string;

interface RequestInit {
    body?: BodyInit | null;
    cache?: RequestCache;
    credentials?: RequestCredentials;
    headers?: HeadersInit;
    integrity?: string;
    keepalive?: boolean;
    method?: string;
    mode?: RequestMode;
    redirect?: RequestRedirect;
    referrer?: string;
    referrerPolicy?: ReferrerPolicy;
    signal?: AbortSignal | null;
    window?: any;
}

declare var Request: {
    prototype: Request;
    new(input: RequestInfo, init?: RequestInit): Request;
};

// -----------------Response-------------------

interface Response extends Body {
    readonly headers: Headers;
    readonly trailer: Promise<Headers>;
    readonly ok: boolean;
    readonly redirected: boolean;
    readonly status: number;
    readonly statusText: string;
    readonly type: ResponseType;
    readonly url: string;
    clone(): Response;
}

declare var Response: {
    prototype: Response;
    new(body?: BodyInit | null, init?: ResponseInit): Response;
    error(): Response;
    redirect(url: string, status?: number): Response;
};

自定义请求对象

除了传入一个 url 字符串作为资源地址, 还可以使用 Request 构造函数来创建一个 request 对象再传递给 fetch

var myHeaders = new Headers();

var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg', myInit);

fetch(myRequest).then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

Requestfetch 接收通用参数

axios

axios 是基于 promise 的 HTTP 库, 可以用在浏览器和 node.js 中

特性

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

安装

npm install axios

使用

import axios from 'axios'

axios 函数

  • axios(config: AxiosRequestConfig): AxiosPromise
  • axios(url: string, config?: AxiosRequestConfig): AxiosPromise

axios 是一个 AxiosStatic

export interface AxiosInstance {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
  // ...
}

export interface AxiosStatic extends AxiosInstance {
    // ...
}

请求配置

如果没有指定方法参数没有指定 url, 配置中必须包含 url.

method 没有配置时默认为 GET

export interface AxiosRequestConfig {
  url?: string;
  method?: Method;
  baseURL?: string;
  transformRequest?: AxiosTransformer | AxiosTransformer[];
  transformResponse?: AxiosTransformer | AxiosTransformer[];
  headers?: any;
  params?: any;
  paramsSerializer?: (params: any) => string;
  data?: any;
  timeout?: number;
  timeoutErrorMessage?: string;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: any) => void;
  onDownloadProgress?: (progressEvent: any) => void;
  maxContentLength?: number;
  validateStatus?: (status: number) => boolean;
  maxRedirects?: number;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
}

export type Method =
  | 'get' | 'GET'
  | 'delete' | 'DELETE'
  | 'head' | 'HEAD'
  | 'options' | 'OPTIONS'
  | 'post' | 'POST'
  | 'put' | 'PUT'
  | 'patch' | 'PATCH'
  | 'link' | 'LINK'
  | 'unlink' | 'UNLINK'

export interface AxiosTransformer {
  (data: any, headers?: any): any;
}

export interface AxiosAdapter {
  (config: AxiosRequestConfig): AxiosPromise<any>;
}

export interface AxiosBasicCredentials {
  username: string;
  password: string;
}

export type ResponseType = 
  | 'arraybuffer' 
  | 'blob' 
  | 'document' 
  | 'json' 
  | 'text' 
  | 'stream'

export interface AxiosProxyConfig {
  host: string;
  port: number;
  auth?: {
    username: string;
    password:string;
  };
  protocol?: string;
}

AxiosPromise

export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> {
}

响应结构

export interface AxiosResponse<T = any>  {
  data: T;
  status: number;
  statusText: string;
  headers: any;
  config: AxiosRequestConfig;
  request?: any;
}

axios 方法

  • export interface AxiosStatic extends AxiosInstance {
      getUri(config?: AxiosRequestConfig): string;
      // ......
    }
    
  • 请求方法

    export interface AxiosInstance {
      request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
      get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
      delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
      head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
      options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
      post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
      put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
      patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
    }
    
    export interface AxiosStatic extends AxiosInstance {
        // ...
    }
    
  • 并发

    export interface AxiosStatic extends AxiosInstance {
      all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
      spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
    }
    
  • 实例创建

    export interface AxiosStatic extends AxiosInstance {
      create(config?: AxiosRequestConfig): AxiosInstance;
    }
    

实例方法

与 axios 静态方法 API 一致, 它们都定义在 AxiosIntance 接口

完整的 AxiosInstance 接口如下

export interface AxiosInstance {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
  defaults: AxiosRequestConfig;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  getUri(config?: AxiosRequestConfig): string;
  request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
  get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
}

配置默认值

可以通过 axios.defaults 对象设置全局 axios 默认值

export interface AxiosInstance {
  defaults: AxiosRequestConfig;
  // ......
}

也可以自定义实例的默认值

// Set config defaults when creating the instance
const instance = axios.create({
  baseURL: 'https://api.example.com'
});

// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

配置优先顺序

axios.defaults < instance.default < 实例方法传入的 config 配置

拦截器

拦截器可以用于在请求/响应被 catchthen 处理前拦截它们

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

axios.interceptors 是一个对象, 其中有 requestresponse 两个属性

export interface AxiosInstance {
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
}

export interface AxiosInterceptorManager<V> {
  use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: any) => any): number;
  eject(id: number): void;
}

如果想要移除拦截器, 可以这样

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

错误处理

AxiosPromise 在 rejected 时将一个 AxiosError 作为一个 reason

export interface AxiosError<T = any> extends Error {
  config: AxiosRequestConfig;
  code?: string;
  request?: any;
  response?: AxiosResponse<T>;
  isAxiosError: boolean;
  toJSON: () => object;
}
axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // The request was made and the server responded with a status code
      // that falls out of the range of 2xx
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // The request was made but no response was received
      // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
      // http.ClientRequest in node.js
      console.log(error.request);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }
    console.log(error.config);
  });

取消

export interface AxiosStatic extends AxiosInstance {
  Cancel: CancelStatic;
  CancelToken: CancelTokenStatic;
  isCancel(value: any): boolean;
  // ......
}

export interface CancelStatic {
  new (message?: string): Cancel;
}

export interface Cancel {
  message: string;
}

export interface CancelTokenStatic {
  new (executor: (cancel: Canceler) => void): CancelToken;
  source(): CancelTokenSource;
}

export interface Canceler {
  (message?: string): void;
}

export interface CancelToken {
  promise: Promise<Cancel>;
  reason?: Cancel;
  throwIfRequested(): void;
}