1:什么是axios
axios是一种实现ajax的方式,基于promise。实现ajax的方式有多种,如jQuery封装的ajax,原生的XMLHttpRequest,以及axios。
Axios,可以理解为ajax i/o system,这不是一种新技术,本质上还是对原生XMLHttpRequest的封装,可用于浏览器和nodejs的HTTP客户端,只不过它是基于Promise的,符合最新的ES规范。具备以下特点:
- 在浏览器中创建XMLHttpRequest请求
- 在node.js中发送http请求
- 支持Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消要求
- 自动转换JSON数据
- 客户端支持防止CSRF/XSRF(跨域请求伪造)
2: axios的请求方式:
- axios(config)
- axios.request(config)
- axios.get(url [,config])
- axios.post(url [,data [,config]])
- axios.put(url [,data [,config]])
- axios.delete(url [,config])
- axios.patch(url [,data [,config]])
- axios.head(url [,config])
3:发送并发请求
通过axios.all(iterable)可实现发送多个请求,参数不一定是数组,只要有iterable接口就行,函数返回的是一个数组。axios.spread(callback)可用于将结果数组展开。//发送多个请求(并发请求),类似于promise.all,若一个请求出错,那就会停止请求const get1 = axios.get('/user/12345');const get2 = axios.get('/user/12345/permission');axios.all([get1,get2]).then(axios.spread((res1,res2)=>{console.log(res1,res2);})).catch(err=>console.log(err))
4:axios API
axios(config)可通过设置一些属性来发送请求
//发送post请求axios({method: 'post', //请求方式,默认是get请求url:'/user/12345', //地址data:{ //参数firstName: 'simon',lastName: 'li'}});
5:全局配置
axios.all([axios({url:'http://123.207.32.32:8000/home/multidata'}),axios({url:'http://123.207.32.32:8000/home/data',params:{type:'sell',page:4}})]).then(results=>{console.log(results);console.log(results[0]);console.log(results[1]);})
在上面的例子里,BaseURL是固定的,可以进行一部分抽取,也可以利用axiox的全局配置。
// 全局配置// 将共有的域名定义为全局配置,使得代码更加简洁axios.defaults.baseURL ='http://123.207.32.32:8000'axios.defaults.timeout = 5000axios.all([axios({url:'/home/multidata'}),axios({url:'/home/data',params:{type:'sell',page:4}})]).then(axios.spread((res1,res2)=>{console.log(res1);console.log(res2);}))
常用的配置选项
但是如果直接设置全局配置的话,那么当某个请求的baseURL等其他信息并不是这个的时候,却没办法灵活应对。这个时候就需要创建axios的实例。
6:axios的实例
为什么要创建axios的实例呢?
- 当我们从axios模块中导入对象时, 使用的实例是默认的实例.
- 当给该实例设置一些默认配置时, 这些配置就被固定下来了.
- 但是后续开发中, 某些配置可能会不太一样.
- 比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.
- 这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.
演示如下:通过多个实例实现配置的互不干涉。
// 4.创建axios的实例const instance1 = axios.create({baseURL:'http://123.207.32.32:8000',timeout:5000})// 进行一次网络请求instance1({url:'/home/multidata'}).then(res=>{console.log(res);})instance1({url:'/home/data',params:{type:'pop',page:1}}).then(res =>{console.log(res);})// 从而实现每一个axios实例有自己独立的配置const instance2 = axios.create({baseURL:'http://123.123.312.132:8000',timeout:10000,})
7:axios框架模块封装
如果不进行封装,那么在每一次要进行网络请求的时候,我们都是单独的书写对于这个axios框架下的对应代码及其代码思路,但如果某一天,使用的这个框架出现大漏洞或者不再维护,我们不得不改用其他框架,那程序员就必须在每一个使用了axios框架的地方进行代码的修改,工作量极大。
因此,对axios进行模块封装,可以使得后面维护的成本减小。
例子:
import axios, { Canceler, AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios';import { ElMessage } from 'element-plus';import qs from 'qs';import pinia, { useStore } from '@/stores';import { useAxiosRequestStore } from '@/stores/axiosRequest';import './loginInterceptor';import { baseUrl, SmartResponse } from './config';// 携带cookie信息axios.defaults.withCredentials = true;// 设置defaults.baseURLaxios.defaults.baseURL = baseUrl;// request 拦截器axios.interceptors.request.use((config: AxiosRequestConfig) => {// vuex记录cancelTokenconst axiosRequestStore = useAxiosRequestStore(pinia);config.cancelToken = new axios.CancelToken((cancel: Canceler) => {axiosRequestStore.pushToken({ cancelToken: cancel });});return config;},(error: any) => Promise.reject(error),);// response 拦截器 请求超时拦截,重新请求axios.interceptors.response.use((response: AxiosResponse) => {const { status } = response;if (status !== 200) {ElMessage({type: 'error',message: `请求服务好像出错了,${status}`,});return Promise.reject(response);}return Promise.resolve(response);},(err: any) => {const { config } = err;if (err.message === 'interrupt') {// 判断是否为路由跳转取消网络请求console.warn('路由跳转取消请求');return Promise.reject(err);} else {// 全局的请求次数,请求的间隙const [RETRY_COUNT, RETRY_DELAY] = [2, 2000];if (config && RETRY_COUNT) {// 设置用于跟踪重试计数的变量config.__retryCount = config.__retryCount || 0;// 检查是否已经把重试的总数用完if (config.__retryCount >= RETRY_COUNT) {return Promise.reject(err);}// 增加重试计数// eslint-disable-next-line no-plusplusconfig.__retryCount++;// 创造新的Promise来处理指数后退const backoff = new Promise<void>((resolve: Function) => {setTimeout(() => {resolve();}, RETRY_DELAY || 1);});// instance重试请求的Promisereturn backoff.then(() => axios(config));}return Promise.reject(err);}},);const store = useStore(pinia);export const request = {/*** get获取数据,通用方法*/doGetPromise(url: string, params?: any, options: any = {}) {const { timeout = 30000, ...arg } = options;return new Promise<SmartResponse>((resolve, reject) => {axios.get(url, {timeout: timeout,...arg,params: {systemId: store.platformInfo.systemId, // 全面接口添加systemId字段userId: store.userInfo.userId,...params,},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** FormData数据上传,文件上传必用*/doPostPromiseForm(url: string, formData: FormData, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {axios.post(url, formData, {headers: {'Content-type': 'multipart/form-data',},params: {userId: store.userInfo.userId,...params,},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** 默认方式提交from表单数据*/doPostPromise(url: string, data: any, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {// 全面接口添加systemId字段if (!params.hasOwnProperty('systemId')) params.systemId = store.platformInfo.systemId;axios.post(url, qs.stringify(data), {headers: {'Content-type': 'application/x-www-form-urlencoded',},params: {userId: store.userInfo.userId,...params,},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** 默认方式提交json数据*/doPostPromiseJson(url: string, data: any, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {axios.post(url, data, {headers: {'Content-type': 'application/json',},params: {userId: store.userInfo.userId,...params,},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** Put方式提交json数据*/doPutPromiseJson(url: string, data: any, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {axios.put(url, data, {headers: {'Content-type': 'application/json',},params: { ...params },}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** Patch方式提交表单数据*/doPatchPromise(url: string, data: any) {return new Promise<SmartResponse>((resolve, reject) => {axios.patch(url, qs.stringify(data), {headers: {'Content-type': 'application/x-www-form-urlencoded',},params: {},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** Patch方式提交json数据*/doPatchPromiseJson(url: string, data: any, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {axios.patch(url, data, {headers: {'Content-type': 'application/json',},params: { ...params },}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},/*** Delete方式*/doDeletePromise(url: string, params: any = {}) {return new Promise<SmartResponse>((resolve, reject) => {axios.delete(url, {headers: {'Content-type': 'application/x-www-form-urlencoded',},params: {...params,},}).then((res: AxiosResponse) => {resolve(res?.data);}).catch((err: AxiosError) => {reject(err);});});},};
8:拦截器interceptors
拦截器是指当发送请求或者得到响应被then或catch处理之前对它们进行拦截,拦截后可对数据做一些处理,比如给请求数据添加头部信息,或对响应数据进行序列化,然后再传给浏览器,这些都可以在拦截器中进行
//添加一个请求拦截器axios.interceptors.request.use(config=>{//在请求之前做一些事return config;},err=>{//请求错误的时候做一些事return Promise.reject(err);});//添加一个响应拦截器axios.interceptors.response.use(response=>{//对返回的数据做一些处理reutrn response;},err=>{//对返回的错误做一些处理return Promise.reject(err);});//移除拦截器const myInterceptor = axios.interceptors.request.use(config=>{return cofig})axios.interceptors.request.eject(myInterceptor);//在一个axios实例中使用拦截器var instance = axios.create();instance.interceptors.request.use(function(){/*...*/});
9:Config配置选项
{//服务器的地址,是必须的选项url: '/user',//请求的方式,若没有则默认是getmethod:'get',//如果url不是绝对地址,则会加上baseURLbaseURL: 'http://localhost:3000/',//transformRequest允许请求的数据在发送至服务器之前进行处理,这个属性只适用于put、post、patch方式//数组的最后一个函数必须返回一个字符串或者一个'ArrayBuffer'或'Stream'或'Buffer' 实例或'ArrayBuffer','Formdata',//若函数中用到了headers,则需要设置headers属性transformRequest: [function(data,headers){//根据需求对数据进行处理return data;}],//transformResponse允许对返回的数据传入then/catch之前进行处理transformResponse:[function(data){//依需要对数据进行处理return data;}],//headers是自定义的要被发送的信息头headers: {'X-Requested-with':'XMLHttpRequest'},//params是请求连接中的请求参数,必须是一个纯对象params:{ID:12345},//paramsSerializer用于序列化参数paramsSerializer: function(params){return Qs.stringify(params,{arrayFormat:'brackets'});},//data是请求时作为请求体的数据——request.body//只适用于put、post、patch请求方法//浏览器:FormData,File,Blob;Node:streamdata:{firstName: 'simon',},//timeout定义请求的时间,单位是毫秒,如果请求时间超过设定时间,请求将停止timeout:1000,//withCredentials表明跨跨域请求书否需要证明。withCredentials:false, //默认值//adapter适配器,允许自定义处理请求//返回一个promiseadapter:function(config){/*...*/},//auth表明HTTP基础的认证应该被使用,并提供证书auth:{username:'simon',password:'123456',},//responseType表明服务器返回的数据类型,这些类型包括:json/blob/document/ arraybuffer/text/streamresponseType: 'json',//proxy定义服务器的主机名和端口号//auth属性表明HTTP基本认证应该跟proxy相连接,并提供证书//这将设置一个'Proxy-Authorization'头(header),覆盖原来自定义的proxy:{host:127.0.0.1,port:8080,auth:{username:'simon',password:'123456'}},//取消请求cancelToken: new CancelToken(cancel=>{})}
