1.先引入
import axios from 'axios'
import qs from 'qs'
import router from '../router';
import store from '../store/index';
// 創建axios實例
var instance = axios.create({ timeout: 1000 * 12});
// 設置post請求頭
// instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; (可以不在這里封裝)
// 攔截器
instance.interceptors.request.use( config => { // 登錄流程控制中,根據本地是否存在token判斷用戶的登錄情況 // 但是即使token存在,也有可能token是過期的,所以在每次的請求頭 中攜帶token // 后台根據攜帶的token判斷用戶的登錄情況,並返回給我們對應的狀態 碼 // 而后我們可以在響應攔截器中,根據狀態碼進行一些統一的操作。 const token = store.state.token; token && (config.headers.Authorization = token); return config; }, error => Promise.error(error) )
// 響應攔截器
instance.interceptors.response.use(
// 請求成功
res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
// 請求失敗
error => {
const { response } = error;
if (response) {
// 請求已發出,但是不在2xx的范圍
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 處理斷網的情況
// eg:請求超時或斷網時,更新state的network狀態
// network狀態在app.vue中控制着一個全局的斷網提示組件的顯示隱藏
// 關於斷網組件中的刷新重新獲取數據,會在斷網組件中說明
store.commit('changeNetwork', false);
}
});
1.去掉了之前get和post方法的封裝,通過創建一個axios實例然后export default方法導出,這樣使用起來更靈活一些。
2.去掉了通過環境變量控制baseUrl的值。考慮到接口會有多個不同域名的情況,所以准備通過js變量來控制接口域名。這點具體在api里會介紹。
3.增加了請求超時,即斷網狀態的處理。說下思路,當斷網時,通過更新vuex中network的狀態來控制斷網提示組件的顯示隱藏。斷網提示一般會有重新加載數據的操作,這步會在后面對應的地方介紹。
4.公用函數進行抽出,簡化代碼,盡量保證單一職責原則。
// 一些方法的封裝
/** * 跳轉登錄頁 * 攜帶當前頁面路由,以期在登錄頁面完成登錄后返回當前頁面 */ const toLogin = () => { router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); }
/**
* 請求失敗后的錯誤統一處理
* @param {Number} status 請求失敗的狀態碼
*/
const errorHandle = (status, other) => {
// 狀態碼判斷
switch (status) {
// 401: 未登錄狀態,跳轉登錄頁
case 401:
toLogin();
break;
// 403 token過期
// 清除token並跳轉登錄頁
case 403:
tip('登錄過期,請重新登錄');
localStorage.removeItem('token');
store.commit('loginSuccess', null);
setTimeout(() => {
toLogin();
}, 1000);
break;
// 404請求不存在
case 404:
tip('請求的資源不存在');
break;
default:
console.log(other);
}}
article.js:
/** * article模塊接口列表 */ import base from './base'; // 導入接口域名列表 import axios from '@/utils/http'; // 導入http中創建的axios實例 import qs from 'qs'; // 根據需求是否導入qs模塊 const article = { // 新聞列表 articleList () { return axios.get(`${base.sq}/topics`); }, // 新聞詳情,演示 articleDetail (id, params) { return axios.get(`${base.sq}/topic/${id}`, { params: params }); }, // post提交 login (params) { return axios.post(`${base.sq}/accesstoken`, qs.stringify(params)); } // 其他接口………… } export default article;
index.js:
這里這里呢新建了一個api文件夾,里面有一個index.js和一個base.js,以及多個根據模塊划分的接口js文件。index.js是一個api的出口,base.js管理接口域名,其他js則用來管理各個模塊的接口。
/** * api接口的統一出口 */ // 文章模塊接口 import article from '@/api/article'; // 其他模塊的接口…… // 導出接口 export default { article, // …… }
base.js:
/** * 接口域名的管理 */ const base = { sq: 'https://xxxx111111.com/api/v1', bd: 'http://xxxxx22222.com/api' } export default base;
.請求的配置更靈活,你可以針對某個需求進行一個不同的配置。關於配置的優先級,axios文檔說的很清楚,這個順序是:在 lib/defaults.js
找到的庫的默認值,然后是實例的 defaults
屬性,最后是請求的 config
參數。后者將優先於前者。
最后,為了方便api的調用,我們需要將其掛載到vue的原型上。在main.js中
import Vue from 'vue' import App from './App' import router from './router' // 導入路由文件 import store from './store' // 導入vuex文件 import api from './api' // 導入api接口 Vue.prototype.$api = api; // 將api掛載到vue的原型上
在頁面中這樣調用接口:
methods: { onLoad(id) { this.$api.article.articleDetail(id, { api: 123 }).then(res=> { // 執行某些操作 }) } }
再提一下斷網的處理,這里只做一個簡單的示例
<template> <div id="app"> <div v-if="!network"> <h3>我沒網了</h3> <div @click="onRefresh">刷新</div> </div> <router-view/> </div> </template> <script> import { mapState } from 'vuex'; export default { name: 'App', computed: { ...mapState(['network']) }, methods: { // 通過跳轉一個空頁面再返回的方式來實現刷新當前頁面數據的目的 onRefresh () { this.$router.replace('/refresh') } } } </script>
// refresh.vue beforeRouteEnter (to, from, next) { next(vm => { vm.$router.replace(from.fullPath) }) }
至此全部完結。文章參考博客喵容 - 和你一起描繪生活。
這下面貼一下自己封裝的。比上面稍簡單些,靈活度不是很高,但適合小白,初學者配置使用,算是很實用吧。
import axios from 'axios' // import qs from 'qs' axios.interceptors.request.use(config => { let append = document.getElementsByName('body') append.innerHTML = '<img style="position:fixed;\n' + ' left:47%;\n' + ' top:40%;\n' + ' transform: translateY(-50%),translateX(-50%);"' + ' src="../../static/img/loading2.gif"/>' return config }, err => { return Promise.resolve(err) }) let base= ‘’ // 接口域名 export const request = (url, params,method,Func,isJson) => { // let _this = this; axios({ method: method, url: `${base}${url}`, data: method=== 'post'? params: '', transformRequest: [function (data) { if(isJson === 1) { // debugger // 判斷是否json格式或者是表單提交形式 return JSON.stringify(data) } let ret = '' for (let it in data) { ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' } return ret // 便於直接取到內部data }], headers: { // 認證和請求方式 'Content-Type': isJson === 1 ? 'application/json' : 'application/x-www-form-urlencoded', 'authorization':sessionStorage.getItem('principal'), 'token':sessionStorage.getItem('token') }, params: method=== 'get'? params: '', }).then(data=>{ console.log(data) if (data.data.code === 200) { Func(data.data.data) }else if(data.data.code === 406){ alert(data.data.message) }else if(data.data.code === 401){ window.location.href=''//上線用這個地址 // window.location.href='/' }else if(data.data.code === 400 || data.data.code === 505 || data.data.code === 404|| data.data.code === 500){ alert('網絡異常') }else if(data.data.code === 4011){ // window.location.href = '' //本地 window.location.href = '' //線上 }else if(data.data.code === 4012){ console.log(1111) request('token/refresh',{ 'authorization': sessionStorage.getItem('principal'), 'refreshToken': sessionStorage.getItem('refreshToken') },'get',(res)=>{ //緩存新的token console.log(res) let token = res.token; let principal = res.principal; let refreshToken = res.refreshToken; sessionStorage.setItem("token", token); sessionStorage.setItem("principal", principal); sessionStorage.setItem("refreshToken", refreshToken); request(url, params,method,Func); }); } }) } // post export const postRequest = (url, params,Func,isJson) => { request(url, params,'post',Func,isJson) } // uploadFileRequest 圖片上傳 export const uploadFileRequest = (url, params) => { return axios({ method: 'post', url: `${base}${url}`, data: params, headers: { 'Content-Type': 'multipart/form-data', 'authorization':sessionStorage.getItem('principal'), 'token':sessionStorage.getItem('token') // 'authorization':'admin', // 'token':'740a1d6be9c14292a13811cabb99950b' } }) } // get export const getRequest = (url, params,Func,isJson) => { request(url, params,'get',Func,isJson) }
main.js 引入 import {getRequest,postRequest} from './api/http' Vue.prototype.$getRequest = getRequest; Vue.prototype.$postRequest = postRequest;
token失效自動刷新,請看我另篇博客:https://www.cnblogs.com/panax/p/13393459.html
至此結束了。相比配置只有兩個文件,比較清晰。