细谈Axios中那些不为人知的秘密!一文读懂Axios

·  阅读 468
细谈Axios中那些不为人知的秘密!一文读懂Axios

 Axios介绍

1、Axios是什么?

Axios是一个基于promise的HTTP库,类似于jQuery的ajax,用于http请求。可以应用于浏览器端和node.js,既可以用于客户端,也可以用于node.js编写的服务端。

2、Axios特性

(1)支持Promise API

(2)拦截请求与响应,比如:在请求前添加授权和响应前做一些事情。

(3)转换请求数据和响应数据,比如:进行请求加密或者响应数据加密。

(4)取消请求

(5)自动转换JSON数据

(6)客户端支持防御XSRF

3、浏览器支持情况

Firefox、Chrome、Safari、Opera、Edge、IE8+。


Axios基本使用

yarn add axios
复制代码
import axios from "axios"
axios.get("/data.json").then(res=>{
    console.log(res)
}).catch(err=>{
    console.log(err)
})
复制代码

打开控制台瞅两眼,已经有数据了 

axios是否使用成功了


Axios常用请求方法

方法列举:get, post, put, patch, delete等。

  • get:一般用户获取数据
  • post:一般用于表单提交与文件上传
  • patch:更新数据(只将修改的数据推送到后端)
  • put:更新数据(所有数据推送到服务端)
  • delete:删除数据

备注:post一般用于新建数据,put一般用于更新数据,patch一般用于数据量较大的时候的数据更新。

1、get方法

方式一

axios
      .get("/data.json", { 
        params: { 
          id: 12
        }
      })
      .then(res => { 
        console.log(res);
      })
      .catch(err => { 
        console.log(err);
      });
复制代码

方式二

axios({ 
      method: "get",
      url: "/data.json",
      params:{ 
          id:12
      }
    }).then(res => { 
      console.log(res);
    });
复制代码

带有参数后get请求实际是http://xxxxx/data.json?id=12,写了这么多结果就是**url加了参数**。

浏览器控制台相关信息介绍:

  1. Request URL:请求URL
  2. Request Method:请求方式

2、post方法

post请求常用的数据请求格式有两种:

1.form-data(常用于表单提交(图片上传、文件上传))

let data = {
  id: 12
};
let formData = new FormData();
for(let key in data){ 
  formData.append(key,data[key])
}
console.log(formData)
axios.post('/data.json',formData).then(res=>{ 
  console.log(res,'formData')
})
复制代码

注意:请求地址Request URL: http://xxxxxxx/data.json,

请求头中Content-Type: multipart/form-data; boundary=——WebKitFormBoundarydgohzXGsZdFwS16Y

参数形式:id:12

2.application/json(常用)

方式一

let data = {

  id: 12
};
axios.post("/data.json", data).then(res=>{ 
  console.log(res, 'post')
});
复制代码

方式二

let data = {
  id: 12
};
axios({ 
  method:'post',
  url:'/data.json',
  data:data
}).then(res=>{ 
  console.log(res)
})
复制代码

注意:请求地址Request URL: http://xxxxxxxx/data.json,

请求头中Content-Type: application/json;charset=UTF-8

参数形式:{ id:12 }

3、put方法

let data = { 
    id: 12
};
axios.put("/data.json", data).then(res=>{ 
    console.log(res, 'put')
});
复制代码

4、patch方法

let data = { 
  id: 12
};
axios.patch("/data.json", data).then(res=>{ 
  console.log(res, 'patch')
});
复制代码

5、delete方法

方式一:params

axios
      .delete("/data.json", { 
        params: { 
          id: 12
        }
      })
      .then(res => { 
        console.log(res, "delete");
      });
let params = { 
      id: 12
    };
    axios({ 
      method:'delete',
      url:'/data.json',
      params:params
    }).then(res=>{ 
      console.log(res)
    })
复制代码

方式二:data

axios
      .delete("/data.json", { 
        data: { 
          id: 12
        }
      })
      .then(res => { 
        console.log(res, "delete");
      });
let data = { 
      id: 12
    };
    axios({ 
      method:'delete',
      url:'/data.json',
      data:data
    }).then(res=>{ 
      console.log(res)
    })
复制代码

注意:params方式会将请求参数拼接在URL上面,Request URL: http://xxxxxxxx/data.json?id=12

参数形式:id:12

Content-Type: text/html; charset=utf-8

data方式不会讲参数拼接,是直接放置在请求体中的,Request URL: http://xxxxxxxx/data.json

参数形式:{id:12}

Content-Type: application/json;charset=UTF-8

总结:上述方法中均对应两种写法:(1)使用别名:形如axios.get();(2)不使用别名形如axios();

6、并发请求

并发请求,就是同时进行多个请求,并统一处理返回值。类似promise.all。

在下面例子中,我们使用axios.all,对data.json/city.json同时进行请求,使用axios.spread,对返回的结果分别进行处理。代码如下:

// 并发请求
axios.all([axios.get("/data.json"), axios.get("/city.json")]).then(
   axios.spread((dataRes, cityRes) => { 
        console.log(dataRes, cityRes);
   })
);
复制代码

注意:axios.all的参数是请求函数的数组,在对应的回调then中,调用axios.spead对返回值进行处理,即可。

并发请求的应用场景:需要同时进行多个请求,并且需要同时处理接口调用的返回值的时候,我们可以使用并发请求。


Axio实例

1、axios实例的创建

比如:后端接口地址有多个(www.test.com、www.example.com),并且超时时长不同(1000ms、2000ms),这个时候,我们可以创建实例。

思路如下:创建多个实例,配置不同的超时时长,用不同的实例去请求不同的接口。使用axios.acreate来创建实例,配置相关信息,进行网络请求。代码如下:

// 实例1
let instance = axios.create({ 
  baseURL:'http://loacalhost:8080',
  timeout:1000
})
instance.get('/data.json').then(res=>{ 
  console.log(res)
})
//实例2
let instance2 = axios.create({ 
  baseURL: "http://loacalhost:8081",
  timeout: 2000
});
instance2.get("/city.json").then(res => { 
  console.log(res);
});
复制代码

备注:此时我们就可以访问http://loacalhost:8080与http://loacalhost:8081两个不同域名的接口,并且使用不同的配置。

2、axios实例的相关配置

(1)配置列表

  • baseURL:请求的域名(基本地址)。

  • timeout:请求的超时时长,超出后后端返回401。

    备注:一般由后端定义,后端的接口需要的处理时长较长的时候,如果请求的时间过长,后端处理不过来,就会阻塞,给服务器造成较大的压力。设置后,可以及时释放掉。

  • url:请求路径。

  • method:请求方法。如:get、post、put、patch、delete等。

  • headers:请求头。

  • params:将请求参数拼接到url上

  • data:将请求参数放置到请求体里 

axios.create({
    baseURL:'', //请求的域名(基本地址)
    timeout:2000, //请求的超时时长,单位毫秒,默认。
    url:'/data.json', //请求路径
    method:'get', //请求方法
    headers:{ 
        token:''
    }, //设置请求头
    params:{ 
    },//将请求参数拼接到url上
    data:{ 
    }, //将请求参数放置到请求体里
});
复制代码

(2)三种配置方式

  • axios全局配置
axios.defaults.baseURL = ‘http://localhost:8080‘

axios.defaults.timeout = 2000
复制代码
  •  axios实例配置
let instance = axios.create();

instance.defaults.timeout = 3000
复制代码
  • axios请求配置
instance.get(‘/data.json’,{

    timeout:5000
})
复制代码

优先级:axios全局配置 < axios实例配置 < axios请求配置

3、常用参数配置的使用方法

举例1:

let instance1 = axios.create({ 
        baseURL:'http://localhost:9090',
        timeout:1000
    })
    instance1.get("/contactList",{ 
        params:{ 
            id:10
        }
    }).then(res=>{ 
        console.log(res)
    })
复制代码

分析:配置的参数为baseURL:‘http://localhost:9090’,timeout:1000,method:‘get’,params:\{ id:10},url:’/contactList’

举例2:

let instance2 = axios.create({ 
        baseURL:'http://localhost:9091',
        timeout:3000
    })
    instance2.get("/contactList",{ 
        timeout:5000
    }).then(res=>{ 
        console.log(res)
    })
复制代码

分析:配置的参数为baseURL:‘http://localhost:9091’,timeout:5000,method:‘get’,url:’/contactList’

注意:最终的有效配置是由优先级高的覆盖优先级低的。


Axios拦截器、错误处理与取消请求

1、拦截器

什么是拦截器?

在请求前或响应被处理前拦截他们,分为两种:请求拦截器与响应拦截器。

拦截器的使用方法

请求拦截器

// 请求拦截器

axios.interceptors.request.use(config => { 
  // 在发送请求前做些什么
  return config;
}, err=>{ 
    // 在请求错误的时候的逻辑处理
    return Promise.reject(err)
});
复制代码

响应拦截器

// 响应拦截器

axios.interceptors.response.use(res => { 
  // 在请求成功后的数据处理
  return res;
}, err=>{ 
    // 在响应错误的时候的逻辑处理
    return Promise.reject(err)
});
复制代码

取消拦截器

let inter = axios.interceptors.request.use(config=>{

    config.header={ 
        auth:true
    }
    return config
})
axios.interceptors.request.eject(inter)
复制代码

实用举例A:登录权限

需要token的接口实例

let instance = axios.create({ });
instance.interceptors.request.use(config=>{ 
    config.headers.token = '';
    return config
})
复制代码

不需要token的接口实例

let newInstance = axios.create({ });
复制代码

实用举例B:移动端开发数据加载loading动画

// 请求的加载动画loading
let instance_phone = axios.create({ });
instance_phone.interceptors.request.use(config=>{ 
    $('#loading').show();
    return config
})
instance_phone.interceptors.response.use(res=>{ 
    $('#loading').hide();
    return res
})
复制代码

备注:实现的效果是请求数据的时候显示loading动画,数据响应后隐藏loading动画。

2、错误处理,拦截

结合请求拦截器与响应拦截器来说,不管是请求错误还是响应错误,都会执行catch方法。

// 请求拦截器
    axios.interceptors.request.use(
      config => { 
        // 在发送请求前做些什么
        return config;
      },
      err => { 
        // 在请求错误的时候的逻辑处理
        return Promise.reject(err);
      }
    );
    // 响应拦截器
    axios.interceptors.response.use(
      res => { 
        // 在请求成功后的数据处理
        return res;
      },
      err => { 
        // 在响应错误的时候的逻辑处理
        return Promise.reject(err);
      }
    ); 
        axios
      .get("data.json")
      .then(res => { 
        console.log(res);
      })
      .catch(err => { 
        console.log(res);
      });
复制代码

错误处理举例

在实际开发中,不会再每次网络请求的时候,使用catch方法,可以添加统一的错误处理方法。代码如下:

// 请求错误处理
    let instance = axios.create({ });
    instance.interceptors.request.use(
      config => { 
        return config;
      },
      err => { 
        // 请求错误的常见状态码:4XX 401-请求超时 404-mot found
        $("#error").show();
        setTimeout(()=>{ 
           $("#error").hide(); 
        }, 2000)
        return Promise.reject(err);
      }
    );
    // 响应错误处理
    instance.interceptors.response.use(
      res => { 
        return res;
      },
      err => { 
        // 响应错误的常见状态码 5XX 500-服务器错误 502-服务器重启
        $("#error").show();
        setTimeout(()=>{ 
           $("#error").hide(); 
        }, 2000)
        return Promise.reject(err);
      }
    );
    instance.get("/data.json").then(res=>{ 
        console.log(res,'请求成功')
    }).catch(err=>{ 
        console.log(err,'除了拦截器设置的处理之外的其他处理')
    })
复制代码

思路分析:首先创建实例,给实例设置请求拦截器与响应拦截器。

  • (1)请求错误的常见状态码以4开头,如401-请求超时、404-接口未找到;
  • (2)响应错误的常见状态码以5开头,如500-服务器错误、502-服务器重启等。
  • (3)处理设置请求拦截器与响应拦截器的操作外,如果还要其他操作,我们可以在请求的时候,在使用catch方法。

3、取消请求(不常用) 

let source = axios.CancelToken.source();

axios
  .get("/data.json", { 
    cancelToken: source.token
  })
  .then(res => { 
    console.log(res);
  }).catch(err=>{ 
      console.log(err)
  })
// 取消请求(参数msg)
  source.cancel('自定的的字符串可选')
复制代码

应用场景

在查询数据的时候,很长时间(3-5s)仍未获取数据,这个时候需要取消请求。

 axios请求的封装与使用

思路分析

axios 请求的封装,无非是为了方便代码管理,我们可以使用抽离拆分的思想,将不同功能模块的接口处理成不同的模块,然后封装一个方法,专门用于数据交互。

第一步:新建 src/service/contactApi.js 文件

const CONTACT_API = { 
    // 获取联系人列表
    getContactList:{ 
        method:'get',
        url:'/contactList'
    },
    // 新建联系人 form-data
    newContactForm:{ 
        method:'post',
        url:'/contact/new/form'
    },
    // 新建联系人 application/json
    newContactJson:{ 
        method:'post',
        url:'/contact/new/json'
    },
    // 编辑联系人
    editContact:{ 
        method:'put',
        url:'/contact/edit'
    },
    // 删除联系人
    delContact:{ 
        method:'delete',
        url:'/contact'
    }
}
export default CONTACT_API
复制代码

备注:该文件的用途只有一个,定义不同的接口请求信息(包含 methodurl等)并导出使用。当接口增加或者删除的时候,只需要定义在该文件中即可。

第二步:新建 src/service/http.js 文件

import axios from 'axios'
import service from './contactApi'
import {  Toast } from 'vant'
// service 循环遍历输出不同的请求方法
let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
const Http = { }; // 包裹请求方法的容器
// 请求格式/参数的统一
for (let key in service) { 
    let api = service[key]; // url method
    // async 作用:避免进入回调地狱
    Http[key] = async function(
        params, // 请求参数 get:url,put,post,patch(data),delete:url
        isFormData = false, // 标识是否是form-data请求
        config = { } // 配置参数
    ) { 
        let newParams = { }
        // content-type是否是form-data的判断
        if (params && isFormData) { 
            newParams = new FormData()
            for (let i in params) { 
                newParams.append(i, params[i])
            }
        } else { 
            newParams = params
        }
        // 不同请求的判断
        let response = { }; // 请求的返回值
        if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
            try { 
                response = await instance[api.method](api.url, newParams, config)
            } catch (err) { 
                response = err
            }
        } else if (api.method === 'delete' || api.method === 'get') { 
            config.params = newParams
            try { 
                response = await instance[api.method](api.url, config)
            } catch (err) { 
                response = err
            }
        }
        return response; // 返回响应值
    }
}
// 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
export default Http
复制代码

具体的思路步骤如下:

首先,我们引入contactApi.js文件,别名定义为 service

import service from './contactApi'
复制代码

 定义新的 axios 实例,针对当前功能模块联系人列表管理 contactList ,配置baseURL基础域名、timeout请求超时时间等等,区别于其他功能模块。

let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
复制代码

定义 http 作为请求方法的容器,配置对应的参数(即请求方法中的其他信息,比如 headerscontent-typetoken等等)。需要注意的是,在其中我们要区分 content-type的形式有两种:form-data 与 application/json,它们的参数配置不同,form-data 形式的参数,我们需要定义 Formdata 对象。具体如下:

let newParams = { }
// content-type是否是form-data的判断
if (params && isFormData) { 
    newParams = new FormData()
    for (let i in params) { 
        newParams.append(i, params[i])
    }
} else { 
    newParams = params
}
复制代码

温馨提示:其中 isFormData 定义为 Boolean 变量,用于标识是否为 FormData 形式。

根据不同的请求方式,发起网络请求,并导出返回值。

// 不同请求的判断
let response = { }; // 请求的返回值
if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
    try { 
        response = await instance[api.method](api.url, newParams, config)
    } catch (err) { 
        response = err
    }
} else if (api.method === 'delete' || api.method === 'get') { 
    config.params = newParams
    try { 
        response = await instance[api.method](api.url, config)
    } catch (err) { 
        response = err
    }
}
return response; // 返回响应值
复制代码

注意:对于不同方法的区别在于:get 与 delete 的参数在 config 中配置,而不是使用 params 。

设置请求拦截器与响应拦截器

/ 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
复制代码

 导出src/service/http.js文件,用于其他地方的引入。

export default Http
复制代码

第三步:在入口文件中导入 http.js ,并挂载到 vue 原型上。

import Http from './service/http'
// 把Http挂载到Vue实例上
Vue.prototype.$Http = Http
复制代码

第四步:在组件中使用封装的请求

// 获取联系人列表
async getList(){ 
    let res = await this.$Http.getContactList()
    this.list = res.data
},
复制代码

注意:在使用的时候,我们需要结合 async 与 await 才能正确使用。具体的使用方法是:

  1. 在定义的网络请求函数前增加 async 标识。
  2. 在接收请求返回数据的时候,增加 await 标识。
  3. 提示:在上面函数中,只有在 res 拿到后才会执行 this.list = res.data; 相当于在对应的 then 中执行的语句,避免了回调地狱。
  4. axios在提交表单请求时会自动设置content-type,此时手动设置时无效的。

总结

在进行项目开发的时候,我们需要对网络请求的方法进行封装,可以有效地减少后期代码维护的难度,建议开发者根据不同的功能模块进行拆分,方便后期代码问题的定位。另外,也能实现代码的低耦合,避免出现更多的重复代码。

​​

 axios请求的封装与使用

思路分析

axios 请求的封装,无非是为了方便代码管理,我们可以使用抽离拆分的思想,将不同功能模块的接口处理成不同的模块,然后封装一个方法,专门用于数据交互。

第一步:新建 src/service/contactApi.js 文件

const CONTACT_API = { 
    // 获取联系人列表
    getContactList:{ 
        method:'get',
        url:'/contactList'
    },
    // 新建联系人 form-data
    newContactForm:{ 
        method:'post',
        url:'/contact/new/form'
    },
    // 新建联系人 application/json
    newContactJson:{ 
        method:'post',
        url:'/contact/new/json'
    },
    // 编辑联系人
    editContact:{ 
        method:'put',
        url:'/contact/edit'
    },
    // 删除联系人
    delContact:{ 
        method:'delete',
        url:'/contact'
    }
}
export default CONTACT_API
复制代码

备注:该文件的用途只有一个,定义不同的接口请求信息(包含 methodurl等)并导出使用。当接口增加或者删除的时候,只需要定义在该文件中即可。

第二步:新建 src/service/http.js 文件

import axios from 'axios'
import service from './contactApi'
import {  Toast } from 'vant'
// service 循环遍历输出不同的请求方法
let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
const Http = { }; // 包裹请求方法的容器
// 请求格式/参数的统一
for (let key in service) { 
    let api = service[key]; // url method
    // async 作用:避免进入回调地狱
    Http[key] = async function(
        params, // 请求参数 get:url,put,post,patch(data),delete:url
        isFormData = false, // 标识是否是form-data请求
        config = { } // 配置参数
    ) { 
        let newParams = { }
        // content-type是否是form-data的判断
        if (params && isFormData) { 
            newParams = new FormData()
            for (let i in params) { 
                newParams.append(i, params[i])
            }
        } else { 
            newParams = params
        }
        // 不同请求的判断
        let response = { }; // 请求的返回值
        if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
            try { 
                response = await instance[api.method](api.url, newParams, config)
            } catch (err) { 
                response = err
            }
        } else if (api.method === 'delete' || api.method === 'get') { 
            config.params = newParams
            try { 
                response = await instance[api.method](api.url, config)
            } catch (err) { 
                response = err
            }
        }
        return response; // 返回响应值
    }
}
// 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
export default Http
复制代码

具体的思路步骤如下:

首先,我们引入contactApi.js文件,别名定义为 service

import service from './contactApi'
复制代码

 定义新的 axios 实例,针对当前功能模块联系人列表管理 contactList ,配置baseURL基础域名、timeout请求超时时间等等,区别于其他功能模块。

let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
复制代码

定义 http 作为请求方法的容器,配置对应的参数(即请求方法中的其他信息,比如 headerscontent-typetoken等等)。需要注意的是,在其中我们要区分 content-type的形式有两种:form-data 与 application/json,它们的参数配置不同,form-data 形式的参数,我们需要定义 Formdata 对象。具体如下:

let newParams = { }
// content-type是否是form-data的判断
if (params && isFormData) { 
    newParams = new FormData()
    for (let i in params) { 
        newParams.append(i, params[i])
    }
} else { 
    newParams = params
}
复制代码

温馨提示:其中 isFormData 定义为 Boolean 变量,用于标识是否为 FormData 形式。

根据不同的请求方式,发起网络请求,并导出返回值。

// 不同请求的判断
let response = { }; // 请求的返回值
if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
    try { 
        response = await instance[api.method](api.url, newParams, config)
    } catch (err) { 
        response = err
    }
} else if (api.method === 'delete' || api.method === 'get') { 
    config.params = newParams
    try { 
        response = await instance[api.method](api.url, config)
    } catch (err) { 
        response = err
    }
}
return response; // 返回响应值
复制代码

注意:对于不同方法的区别在于:get 与 delete 的参数在 config 中配置,而不是使用 params 。

设置请求拦截器与响应拦截器

/ 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
复制代码

 导出src/service/http.js文件,用于其他地方的引入。

export default Http
复制代码

第三步:在入口文件中导入 http.js ,并挂载到 vue 原型上。

import Http from './service/http'
// 把Http挂载到Vue实例上
Vue.prototype.$Http = Http
复制代码

第四步:在组件中使用封装的请求

// 获取联系人列表
async getList(){ 
    let res = await this.$Http.getContactList()
    this.list = res.data
},
复制代码

注意:在使用的时候,我们需要结合 async 与 await 才能正确使用。具体的使用方法是:

  1. 在定义的网络请求函数前增加 async 标识。
  2. 在接收请求返回数据的时候,增加 await 标识。
  3. 提示:在上面函数中,只有在 res 拿到后才会执行 this.list = res.data; 相当于在对应的 then 中执行的语句,避免了回调地狱。
  4. axios在提交表单请求时会自动设置content-type,此时手动设置时无效的。

总结

在进行项目开发的时候,我们需要对网络请求的方法进行封装,可以有效地减少后期代码维护的难度,建议开发者根据不同的功能模块进行拆分,方便后期代码问题的定位。另外,也能实现代码的低耦合,避免出现更多的重复代码。

 axios请求的封装与使用

思路分析

axios 请求的封装,无非是为了方便代码管理,我们可以使用抽离拆分的思想,将不同功能模块的接口处理成不同的模块,然后封装一个方法,专门用于数据交互。

第一步:新建 src/service/contactApi.js 文件

const CONTACT_API = { 
    // 获取联系人列表
    getContactList:{ 
        method:'get',
        url:'/contactList'
    },
    // 新建联系人 form-data
    newContactForm:{ 
        method:'post',
        url:'/contact/new/form'
    },
    // 新建联系人 application/json
    newContactJson:{ 
        method:'post',
        url:'/contact/new/json'
    },
    // 编辑联系人
    editContact:{ 
        method:'put',
        url:'/contact/edit'
    },
    // 删除联系人
    delContact:{ 
        method:'delete',
        url:'/contact'
    }
}
export default CONTACT_API
复制代码

备注:该文件的用途只有一个,定义不同的接口请求信息(包含 methodurl等)并导出使用。当接口增加或者删除的时候,只需要定义在该文件中即可。

第二步:新建 src/service/http.js 文件

import axios from 'axios'
import service from './contactApi'
import {  Toast } from 'vant'
// service 循环遍历输出不同的请求方法
let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
const Http = { }; // 包裹请求方法的容器
// 请求格式/参数的统一
for (let key in service) { 
    let api = service[key]; // url method
    // async 作用:避免进入回调地狱
    Http[key] = async function(
        params, // 请求参数 get:url,put,post,patch(data),delete:url
        isFormData = false, // 标识是否是form-data请求
        config = { } // 配置参数
    ) { 
        let newParams = { }
        // content-type是否是form-data的判断
        if (params && isFormData) { 
            newParams = new FormData()
            for (let i in params) { 
                newParams.append(i, params[i])
            }
        } else { 
            newParams = params
        }
        // 不同请求的判断
        let response = { }; // 请求的返回值
        if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
            try { 
                response = await instance[api.method](api.url, newParams, config)
            } catch (err) { 
                response = err
            }
        } else if (api.method === 'delete' || api.method === 'get') { 
            config.params = newParams
            try { 
                response = await instance[api.method](api.url, config)
            } catch (err) { 
                response = err
            }
        }
        return response; // 返回响应值
    }
}
// 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
export default Http
复制代码

具体的思路步骤如下:

 首先,我们引入contactApi.js文件,别名定义为 service

import service from './contactApi'
复制代码

 定义新的 axios 实例,针对当前功能模块联系人列表管理 contactList ,配置baseURL基础域名、timeout请求超时时间等等,区别于其他功能模块。

let instance = axios.create({ 
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
})
复制代码

定义 http 作为请求方法的容器,配置对应的参数(即请求方法中的其他信息,比如 headerscontent-typetoken等等)。需要注意的是,在其中我们要区分 content-type的形式有两种:form-data 与 application/json,它们的参数配置不同,form-data 形式的参数,我们需要定义 Formdata 对象。具体如下:

let newParams = { }
// content-type是否是form-data的判断
if (params && isFormData) { 
    newParams = new FormData()
    for (let i in params) { 
        newParams.append(i, params[i])
    }
} else { 
    newParams = params
}
复制代码

温馨提示:其中 isFormData 定义为 Boolean 变量,用于标识是否为 FormData 形式。

根据不同的请求方式,发起网络请求,并导出返回值。

// 不同请求的判断
let response = { }; // 请求的返回值
if (api.method === 'put' || api.method === 'post' || api.method === 'patch') { 
    try { 
        response = await instance[api.method](api.url, newParams, config)
    } catch (err) { 
        response = err
    }
} else if (api.method === 'delete' || api.method === 'get') { 
    config.params = newParams
    try { 
        response = await instance[api.method](api.url, config)
    } catch (err) { 
        response = err
    }
}
return response; // 返回响应值
复制代码

注意:对于不同方法的区别在于:get 与 delete 的参数在 config 中配置,而不是使用 params 。

设置请求拦截器与响应拦截器

/ 拦截器的添加
// 请求拦截器
instance.interceptors.request.use(config => { 
        // 发起请求前做些什么
        Toast.loading({ 
            mask: false,
            duration: 0, // 一直存在
            forbidClick: true, // 禁止点击
            message: '加载中...'
        })
        return config
    }, () => { 
        // 请求错误
        Toast.clear()
        Toast('请求错误,请求稍后重试')
    })
    // 响应拦截器
instance.interceptors.response.use(res => { 
    // 请求成功
    Toast.clear()
    return res.data
}, () => { 
    Toast.clear()
    Toast('请求错误,请求稍后重试')
})
复制代码

 导出src/service/http.js文件,用于其他地方的引入。

export default Http
复制代码

第三步:在入口文件中导入 http.js ,并挂载到 vue 原型上。

import Http from './service/http'
// 把Http挂载到Vue实例上
Vue.prototype.$Http = Http
复制代码

第四步:在组件中使用封装的请求

// 获取联系人列表
async getList(){ 
    let res = await this.$Http.getContactList()
    this.list = res.data
},
复制代码

注意:在使用的时候,我们需要结合 async 与 await 才能正确使用。具体的使用方法是:

  1. 在定义的网络请求函数前增加 async 标识。
  2. 在接收请求返回数据的时候,增加 await 标识。
  3. 提示:在上面函数中,只有在 res 拿到后才会执行 this.list = res.data; 相当于在对应的 then 中执行的语句,避免了回调地狱。
  4. axios在提交表单请求时会自动设置content-type,此时手动设置时无效的。

总结

在进行项目开发的时候,我们需要对网络请求的方法进行封装,可以有效地减少后期代码维护的难度,建议开发者根据不同的功能模块进行拆分,方便后期代码问题的定位。另外,也能实现代码的低耦合,避免出现更多的重复代码。

分类:
前端