基于axios库进行适合自己项目的二次封装

基于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的统一管理和使用

  1. api/index.js
    统一管理项目api接口,只要是有请求的地方都得引入此文件,默认导出的是一个对象,对象中是各个模块,模块中才是具体的每一个请求的api接口。
/**
 * 统一管理项目api接口的出口 
 * 把api接口根据功能划分为多个模块
 * 利于多人协作开发,比如一个人只负责一个模块的开发等
 * 还能方便每个模块中接口的命名
 */

/* 主页模块接口 */
import home from "./home.js"

/* 我的模块接口 */
import profile from "./profile.js"

/* 其他模块接口.. */

/* 导出接口 */
export default {
	home,
	profile,
}
  1. api/base.js
    对项目中不同环境、不同情况下的URL在此统一进行管理,导出一个对象,当页面引入此文件之后,应该在请求的URL项中使用模板字符串${baseURL.prod}的方式获取到自己负责模块的请求路径。
/**
 * 接口域名的统一管理 
 */
const baseURL = {
	prod:'https://xxxx111111.com/api/v1',   
	dev:'https://xxxx111111.com/api/',
	// ...
}

export default baseURL;
  1. 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;
  1. 在项目入口文件main.js进行原型挂载
import api from "./api/index.js"
import router from './router' // 导入路由文件
import store from './store' // 导入vuex文件
Vue.prototype.$api = api; // 方便在任何一个组件中通过$api请求
  1. 在页面中进行使用 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)        
    })    
}
posted @ 2021-07-24 23:33  小高同学1997  阅读(376)  评论(0编辑  收藏  举报