基于axios库进行适合自己项目的二次封装
一、对第三方库进行二次封装的好处
对第三方库的依赖低,一旦第三方库不再维护或者出现问题,只需要改动封装的这个实例即可,而不用去每一个请求的地方进行修改。
二、二次封装的时候文件夹的功能划分
request文件夹下的http.js主要用于封装axios实例
api文件夹主要用于对项目中关于网络请求这一块的集中管理和配置
- index.js 所有项目api接口的统一出口,页面中引入此文件后进行统一请求
- base.js 此文件主要用于对多个环境配置不同的baseURL
- home.js 此文件中基于http.js中导出的axios实例进行home主页模块的所有api请求
- profile.js 基于http.js中导出的axios实例进行profile个人模块的所有api请求
——————request
——————http.js
——————api
——————index.js
——————base.js
——————home.js
——————profile.js
三、对于axios实例的二次封装
1. 基于antd组件库的message组件进行统一错误提示方法封装
在调用时可以传入duration,这里默认为1.5s
/**
* 错误提示函数
* 基于antd的message全局提示组件进行二次封装
* 默认延时为1500ms后关闭
*/
import {message} from ‘antd‘
const tip = (content,duration=1500)=>{
message.error({
content,
duration,
})
}
2. 封装跳转至登录页面并携带当前页面路径的方法
/**
* 跳转登录页
* 需要携带当前页面路由 登录成功之后返回当前页面
*/
import router from ‘../router‘
const toLogin = ()=>{
router.replace({
path:"/login",
query:{
redirect:router.currentRoute.fullPath
}
})
}
3. 封装生成axios实例时的默认配置项
主要基于process.env.NODE_ENV环境变量来判断baseURL的值,这是一种方法,其实也可以采用直接将所有请求地址放在base.js文件中统一管理,也是可以的。
/**
* 设置默认请求配置,包含请求baseURL、请求过期时间等
*/
// 基于node环境变量设置接口baseURL
const env = process.env.NODE_ENV;
let BASE_URL;
if(env==="development"){
BASE_URL = "http://httpbin.org";
}else if(env==="production"){
BASE_URL = "http://httpbin.org";
}else if(env==="debug"){
BASE_URL = "http://httpbin.org";
}
// 设置请求过期时间
const TIMEOUT = 5000;
const requestConfig = {
baseURL:BASE_URL,
timeout:TIMEOUT,
}
/**
* 基于默认配置创建axios请求实例
* 如果是post请求,需要加上Content-Type的请求头配置
*/
let instance = axios.create(requestConfig);
const CONTENT_TYPE = "application/x-www-form-urlencoded;charset=UTF-8"
instance.defaults.headers.post[‘Content-Type‘] = CONTENT_TYPE;
4. 封装请求错误之后的统一错误处理
/**
* 请求错误后的统一处理
* @param {Number} status 请求失败后服务器返回的状态码
* @param {String} otherMessage 请求失败后服务器的错误信息描述
* 此参数需要前后端进行对接,确认好不同状态码对应的不同情况
*/
const errorHandle = (errStatus,otherMessage)=>{
// 判断状态码
switch(errStatus){
// 401 未登录状态,直接跳转至登录页
case 401:
toLogin();
break;
// 403 token过期,清除本地token后跳转至
case 403:
tip(‘登录过期,请重新选择‘);
localStorage.removeItem(‘token‘);
setTimeout(()=>{
toLogin();
},1000)
break;
// 404 请求资源不存在
case 404:
tip(‘请求资源不存在‘);
break;
default:
console.log(otherMessage);
}
}
5. 封装请求拦截器
/**
* 添加请求拦截器
* 每次请求前,如果存在token则在请求头中携带token
*/
instance.interceptors.request.use(
config=>{
/*
* 基于本地localStorage是否存在token判断用户是否登录
* 如果没有token,那么用户未登录,请求还是会发出,但是服务器会返回401状态码,从而统一导航至login登录页面
* 如果有token,也不能保证token是否过期,那么在发送请求前在请求头的Authorization字段添加token,交给服务器进行判断,假设token过期,则返回403状态码,延时1s之后导航至login登录页面
*/
const token = localStorage.getItem(‘token‘);
token && (config.headers.Authorization = token);
return config;
},
err=>{
return Promise.reject(err);
}
)
6. 封装响应拦截器
/**
* 添加响应拦截器
* 响应成功 200
* 响应失败 非200状态码
* 断网
*/
instance.interceptors.response.use(
res=>{
if(res.status===200){
console.log(‘状态码为200,请求成功‘);
return res.data;
}else{
return Promise.reject(res);
}
},
err=>{
const {response} = err;
// 请求已经发出 但是状态码不为200
if(response){
errorHandle(response.status,res.data.message);
return Promise.reject(response);
}else{
// 处理断网情况
// 请求超时或者断网时,更新state中的newWork状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
if(!window.navigator.onLine){
console.log(‘网络已断开,请检查网络‘)
// store.commit(‘changeNetWork‘,false);
}else{
return Promise.reject(err)
}
}
}
)
四、对于项目api的统一管理和使用
- api/index.js
统一管理项目api接口,只要是有请求的地方都得引入此文件,默认导出的是一个对象,对象中是各个模块,模块中才是具体的每一个请求的api接口。
/**
* 统一管理项目api接口的出口
* 把api接口根据功能划分为多个模块
* 利于多人协作开发,比如一个人只负责一个模块的开发等
* 还能方便每个模块中接口的命名
*/
/* 主页模块接口 */
import home from "./home.js"
/* 我的模块接口 */
import profile from "./profile.js"
/* 其他模块接口.. */
/* 导出接口 */
export default {
home,
profile,
}
- api/base.js
对项目中不同环境、不同情况下的URL在此统一进行管理,导出一个对象,当页面引入此文件之后,应该在请求的URL项中使用模板字符串${baseURL.prod}
的方式获取到自己负责模块的请求路径。
/**
* 接口域名的统一管理
*/
const baseURL = {
prod:‘https://xxxx111111.com/api/v1‘,
dev:‘https://xxxx111111.com/api/‘,
// ...
}
export default baseURL;
- home.js
此文件专门负责对和home模块相关业务的网络请求进行处理,首先需要引入base文件获取请求地址;并且需要引入前面封装好的可以进行请求拦截、统一错误处理的axios实例,然后基于此实例进行网络请求。
默认导出一个home对象,此对象中存放的都是一个个的方法,每一个方法在执行之后都会return出去一个axios请求返回的Pomise对象,在调用此方法的时候就可以基于then和catch去处理请求数据和错误。
import baseURL from "./base.js"
/* 引入二次封装好的axios实例 */
import axios from "../request/http.js"
/* 依次定义home模块下api接口 */
const home = {
// 首页banner列表
bannerList(){
return axios.get(`${baseURL.dev}/banner`)
}
// 首页商品详情
goodsDetail(id,params){
return axios.get(`${baseURL.dev}/detail/${id}`,{
params:params
})
}
// post提交
login(params){
return axios.post(`${baseURL.dev}/login`,JSON.stringify(params))
}
}
export default home;
- 在项目入口文件main.js进行原型挂载
import api from "./api/index.js"
import router from ‘./router‘ // 导入路由文件
import store from ‘./store‘ // 导入vuex文件
Vue.prototype.$api = api; // 方便在任何一个组件中通过$api请求
- 在页面中进行使用 index.vue
export default new Vue({
data(){
return {
id:1,
page:1
}
},
created(){
this.getBannerList();
this.getGoodsDetail(id,page);
},
methods:{
getBannerList(){
this.$api.home.bannerList().then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
});
}
getGoodsDetail(id,page){
this.$api.home.goodsDetail(id,page).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
});
}
}
})
五、对于网络断开情况的处理
当我们在发起请求的时候,如果需要请求超时或者网络断开的情况,此时基于window.navigator.onLine来进行判断,如果已经断开网络,此时向vuex中提交一个改变网络状态的请求。
全局组件:NetWorkOffLine 网络断开时页面展示的组件 基于布尔值来进行控制
默认情况下,该组件隐藏。一般网络断开,提交mutation到vuex中,改变vuex中state中布尔值为false,此时该组件展示,其他页面中组件隐藏。
<!-- 页面中处理 -->
<div>
<NetWorkOffLine v-if="store.state.isShowOffLine"></NetWorkOffLine>
<div v-else>
// 页面功能组件
</div>
</div>
<!-- 拦截器中进行提交修改 -->
store.commit(‘changeNetWork‘,true);
<!-- vuex中修改state -->
new Vuex({
state:{
isShowOffLine:false
},
mutations:{
changeNetWork(state,payload){
state.isShowOffLine = payload;
}
}
})
该组件中有一个点击刷新的按钮,当点击刷新的时候,我们通过跳转refesh页面然后立即返回的方式来实现重新获取数据的操作。因此我们需要新建一个refresh.vue页面,并在其beforeRouteEnter钩子中再返回当前页面。
// refresh.vue中路由导航守卫
beforeRouteEnter (to, from, next) {
next(vm => {
vm.$router.replace(from.fullPath)
})
}