axios詳解以及完整封裝方法

2023-07-15 18:00:54

"""


一、axios是什麼

Axios 是一個基於 promise 網路請求庫,作用於node.js 和瀏覽器中。 它是 isomorphic 的(即同一套程式碼可以執行在瀏覽器和node.js中)。在伺服器端它使用原生 node.js http 模組, 而在使用者端 (瀏覽端) 則使用 XMLHttpRequests。


axios有以下特性:

  • 從瀏覽器建立 XMLHttpRequests
  • 從 node.js 建立 http 請求
  • 支援 Promise API
  • 攔截請求和響應
  • 轉換請求和響應資料
  • 取消請求
  • 自動轉換JSON資料
  • 使用者端支援防禦XSRF

axios可以請求的方法:

  • get:獲取資料,請求指定的資訊,返回實體物件
  • post:向指定資源提交資料(例如表單提交或檔案上傳)
  • put:更新資料,從使用者端向伺服器傳送的資料取代指定的檔案的內容
  • patch:更新資料,是對put方法的補充,用來對已知資源進行區域性更新
  • delete:請求伺服器刪除指定的資料
  • head:獲取報文首部

請求方法別名

為了方便起見,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])

二.axios範例及設定方法

1.建立axios範例
axios.create([config])

可以同時建立多個axios範例。

範例程式碼
"""

const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

"""
實體方法

以下是可用的實體方法。指定的設定將與範例的設定合併。

  • axios#request(config)

  • axios#get(url[, config])

  • axios#delete(url[, config])

  • axios#head(url[, config])

  • axios#options(url[, config])

  • axios#post(url[, data[, config]])

  • axios#put(url[, data[, config]])

  • axios#patch(url[, data[, config]])

  • axios#getUri([config])


2.設定方法

設定物件常用的設定項:

這些是建立請求時可以用的設定選項。只有 url 是必需的。如果沒有指定 method,請求將預設使用 GET 方法。更多設定項請檢視官方檔案

"""

{
  // 路徑url
  url: '/user',

  // 請求方法,預設get
  method: 'get', 

  //基礎url,最終請求的url是 baseURL+url拼接,所以再全域性設定預設,可以使得傳送請求時的url變得簡潔
  baseURL: 'https://some-domain.com/api/',

  //設定請求頭
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  //設定請求url的query引數,可以使得url簡潔。
  //比如url是https://some-domain.com/api/user  然後params如下設定,那麼最終的url是:
  //https://some-domain.com/api/user?ID=12345&name=Jack
  params: {
	ID: 12345,
	name:"Jack"
  },

 //設定請求體
  data: {
	firstName: 'Fred'
  },

  //設定請求的另外一種格式,不過這個是直接設定字串的
  data: 'Country=Brasil&City=Belo Horizonte',

 //請求超時,單位毫秒,預設0,不超時。
  timeout: 1000,

  //響應資料型別,預設json
  responseType: 'json', 

  //響應資料的編碼規則,預設utf-8
  responseEncoding: 'utf8',

	//響應體的最大長度 
  maxContentLength: 2000,

  // 請求體的最大長度
  maxBodyLength: 2000,

  //設定響應狀態碼為多少時是成功,呼叫resolve,否則呼叫reject失敗
  //預設是大於等於200,小於300
  validateStatus: function (status) {
	return status >= 200 && status < 300; 
  },

"""


預設設定

可以設定全域性預設設定,是為了避免多種重複設定在不同請求中重複,比如baseURL、timeout等,這裡設定baseURL。


全域性 axios 預設值
"""

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

"""
自定義範例預設值

"""

// 建立範例時設定預設值
const instance = axios.create({
  baseURL: 'https://api.example.com'
});

// 建立範例後修改預設值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

""""


設定的優先順序

設定將會按優先順序進行合併。它的順序是:在 lib/defaults.js 中找到的庫預設值,然後是範例的 defaults 屬性,最後是請求的 config 引數。後面的優先順序要高於前面的。


三、攔截器

在請求或響應被 then 或 catch 處理前攔截它們,自定義的axios範例也可新增攔截器,如:
"""

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

"""
請求攔截器

範例程式碼:

"""

// 新增請求攔截器
axios.interceptors.request.use(function (config) {
	// 在傳送請求之前做些什麼
	return config;
  }, function (error) {
	// 對請求錯誤做些什麼
	return Promise.reject(error);
  });

"""
響應攔截器

範例程式碼:

"""

// 新增響應攔截器
axios.interceptors.response.use(function (response) {
	// 2xx 範圍內的狀態碼都會觸發該函數。
	// 對響應資料做點什麼
	return response;
  }, function (error) {
	// 超出 2xx 範圍的狀態碼都會觸發該函數。
	// 對響應錯誤做點什麼
	return Promise.reject(error);
  });

"""
取消攔截器

範例程式碼:

"""

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

"""


四、取消請求

注意:從 v0.22.0 開始,Axios 支援以 fetch API 方式—— AbortController 取消請求,CancelToken API被棄用

這裡我們兩種方法都介紹一下,使用過程中能用 AbortController 就儘量別用 CancelToken

AbortController

"""

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消請求
controller.abort()

"""
CancelToken

"""

let source = axios.CancelToken.source();

axios.get('/users/12345',{
				cancelToken: source.token
			}).then(res=>{
				console.log(res)
			}).catch(err=>{
				//取消請求後會執行該方法
				console.log(err)
			})

//取消請求,引數可選,該引數資訊會傳送到請求的catch中
source.cancel('取消後的資訊');

"""
也可以通過傳遞一個 executor 函數到 CancelToken 的建構函式來建立一個 cancel token:

"""

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
	// executor 函數接收一個 cancel 函數作為引數
	cancel = c;
  })
});

// 取消請求
cancel();

"""
注意: 可以使用同一個 cancel token 或 signal 取消多個請求


五、axios封裝

先設計我們想要這個通用請求能達到什麼樣的效果:

  • 優化設定,設定預設設定項(responseType、跨域攜帶cookie、token、超時設定)
  • 統一設定請求頭
  • 根據環境設定 baseURL
  • 通過 Axios 方法直接發起請求
  • 新增請求攔截器
  • 新增響應攔截器
  • 匯出 Promise 物件
  • 封裝 Post 方法,精簡 post 請求方式
  • 封裝 Get 方法,精簡 get 請求方式
  • 請求成功,設定業務狀態碼
  • 全域性的loading設定

一、axios的封裝
在vue專案中,和後臺互動獲取資料這塊,我們通常使用的是axios庫,它是基於promise的http庫,可執行在瀏覽器端和node.js中。他有很多優秀的特性,例如攔截請求和響應、取消請求、轉換json、使用者端防禦XSRF等。所以我們的尤大大也是果斷放棄了對其官方庫vue-resource的維護,直接推薦我們使用axios庫。如果還對axios不瞭解的,可以移步axios檔案。

安裝
npm install axios; // 安裝axios
引入
一般我會在專案的src目錄中,新建一個request資料夾,然後在裡面新建一個http.js和一個api.js檔案。http.js檔案用來封裝我們的axios,api.js用來統一管理我們的介面。

// 在http.js中引入axios
import axios from 'axios'; // 引入axios
import QS from 'qs'; // 引入qs模組,用來序列化post型別的資料,後面會提到
// vant的toast提示框元件,大家可根據自己的ui元件更改。
import { Toast } from 'vant'; 
環境的切換
我們的專案環境可能有開發環境、測試環境和生產環境。我們通過node的環境變數來匹配我們的預設的介面url字首。axios.defaults.baseURL可以設定axios的預設請求地址就不多說了。

// 環境的切換
if (process.env.NODE_ENV == 'development') {    
	axios.defaults.baseURL = 'https://www.baidu.com';} 
else if (process.env.NODE_ENV == 'debug') {    
	axios.defaults.baseURL = 'https://www.ceshi.com';
} 
else if (process.env.NODE_ENV == 'production') {    
	axios.defaults.baseURL = 'https://www.production.com';
}
設定請求超時
通過axios.defaults.timeout設定預設的請求超時時間。例如超過了10s,就會告知使用者當前請求超時,請重新整理等。

axios.defaults.timeout = 10000;
post請求頭的設定
post請求的時候,我們需要加上一個請求頭,所以可以在這裡進行一個預設的設定,即設定post的請求頭為application/x-www-form-urlencoded;charset=UTF-8

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
請求攔截
我們在傳送請求前可以進行一個請求的攔截,為什麼要攔截呢,我們攔截請求是用來做什麼的呢?比如,有些請求是需要使用者登入之後才能存取的,或者post請求的時候,我們需要序列化我們提交的資料。這時候,我們可以在請求被傳送之前進行一個攔截,從而進行我們想要的操作。

請求攔截
// 先匯入vuex,因為我們要使用到裡面的狀態物件
// vuex的路徑根據自己的路徑去寫
import store from '@/store/index';

// 請求攔截器axios.interceptors.request.use(    
	config => {        
		// 每次傳送請求之前判斷vuex中是否存在token        
		// 如果存在,則統一在http請求的header都加上token,這樣後臺根據token判斷你的登入情況
		// 即使本地存在token,也有可能token是過期的,所以在響應攔截器中要對返回狀態進行判斷 
		const token = store.state.token;        
		token && (config.headers.Authorization = token);        
		return config;    
	},    
	error => {        
		return Promise.error(error);    
})
這裡說一下token,一般是在登入完成之後,將使用者的token通過localStorage或者cookie存在本地,然後使用者每次在進入頁面的時候(即在main.js中),會首先從本地儲存中讀取token,如果token存在說明使用者已經登陸過,則更新vuex中的token狀態。然後,在每次請求介面的時候,都會在請求的header中攜帶token,後臺人員就可以根據你攜帶的token來判斷你的登入是否過期,如果沒有攜帶,則說明沒有登入過。這時候或許有些小夥伴會有疑問了,就是每個請求都攜帶token,那麼要是一個頁面不需要使用者登入就可以存取的怎麼辦呢?其實,你前端的請求可以攜帶token,但是後臺可以選擇不接收啊!

響應的攔截
// 響應攔截器
axios.interceptors.response.use(    
	response => {   
		// 如果返回的狀態碼為200,說明介面請求成功,可以正常拿到資料     
		// 否則的話丟擲錯誤
		if (response.status === 200) {            
			return Promise.resolve(response);        
		} else {            
			return Promise.reject(response);        
		}    
	},    
	// 伺服器狀態碼不是2開頭的的情況
	// 這裡可以跟你們的後臺開發人員協商好統一的錯誤狀態碼    
	// 然後根據返回的狀態碼進行一些操作,例如登入過期提示,錯誤提示等等
	// 下面列舉幾個常見的操作,其他需求可自行擴充套件
	error => {            
		if (error.response.status) {            
			switch (error.response.status) {                
				// 401: 未登入
				// 未登入則跳轉登入頁面,並攜帶當前頁面的路徑
				// 在登入成功後返回當前頁面,這一步需要在登入頁操作。                
				case 401:                    
					router.replace({                        
						path: '/login',                        
						query: { 
							redirect: router.currentRoute.fullPath 
						}
					});
					break;

            // 403 token過期
            // 登入過期對使用者進行提示
            // 清除本地token和清空vuex中token物件
            // 跳轉登入頁面                
            case 403:
                 Toast({
                    message: '登入過期,請重新登入',
                    duration: 1000,
                    forbidClick: true
                });
                // 清除token
                localStorage.removeItem('token');
                store.commit('loginSuccess', null);
                // 跳轉登入頁面,並將要瀏覽的頁面fullPath傳過去,登入成功後跳轉需要存取的頁面 
                setTimeout(() => {                        
                    router.replace({                            
                        path: '/login',                            
                        query: { 
                            redirect: router.currentRoute.fullPath 
                        }                        
                    });                    
                }, 1000);                    
                break; 

            // 404請求不存在
            case 404:
                Toast({
                    message: '網路請求不存在',
                    duration: 1500,
                    forbidClick: true
                });
                break;
            // 其他錯誤,直接丟擲錯誤提示
            default:
                Toast({
                    message: error.response.data.message,
                    duration: 1500,
                    forbidClick: true
                });
        }
        return Promise.reject(error.response);
    }
}    
});
響應攔截器很好理解,就是伺服器返回給我們的資料,我們在拿到之前可以對他進行一些處理。例如上面的思想:如果後臺返回的狀態碼是200,則正常返回資料,否則的根據錯誤的狀態碼型別進行一些我們需要的錯誤,其實這裡主要就是進行了錯誤的統一處理和沒登入或登入過期後調整登入頁的一個操作。

要注意的是,上面的Toast()方法,是我引入的vant庫中的toast輕提示元件,你根據你的ui庫,對應使用你的一個提示元件。

封裝get方法和post方法
我們常用的ajax請求方法有get、post、put等方法,相信小夥伴都不會陌生。axios對應的也有很多類似的方法,不清楚的可以看下檔案。但是為了簡化我們的程式碼,我們還是要對其進行一個簡單的封裝。下面我們主要封裝兩個方法:get和post。

get方法:我們通過定義一個get函數,get函數有兩個引數,第一個參數列示我們要請求的url地址,第二個引數是我們要攜帶的請求引數。get函數返回一個promise物件,當axios其請求成功時resolve伺服器返回 值,請求失敗時reject錯誤值。最後通過export丟擲get函數。

/**
 * get方法,對應get請求
 * @param {String} url [請求的url地址]
 * @param {Object} params [請求時攜帶的引數]
 */
export function get(url, params){    
	return new Promise((resolve, reject) =>{        
		axios.get(url, {            
			params: params        
		}).then(res => {
			resolve(res.data);
		}).catch(err =>{
			reject(err.data)        
	})    
});}
post方法:原理同get基本一樣,但是要注意的是,post方法必須要使用對提交從引數物件進行序列化的操作,所以這裡我們通過node的qs模組來序列化我們的引數。這個很重要,如果沒有序列化操作,後臺是拿不到你提交的資料的。這就是文章開頭我們import QS from 'qs';的原因。如果不明白序列化是什麼意思的,就百度一下吧,答案一大堆。

/** 
 * post方法,對應post請求 
 * @param {String} url [請求的url地址] 
 * @param {Object} params [請求時攜帶的引數] 
 */
export function post(url, params) {
	return new Promise((resolve, reject) => {
		 axios.post(url, QS.stringify(params))
		.then(res => {
			resolve(res.data);
		})
		.catch(err =>{
			reject(err.data)
		})
	});
}
這裡有個小細節說下,axios.get()方法和axios.post()在提交資料時引數的書寫方式還是有區別的。區別就是,get的第二個引數是一個{},然後這個物件的params屬性值是一個引數物件的。而post的第二個引數就是一個引數物件。兩者略微的區別要留意哦!

axios的封裝基本就完成了,下面再簡單說下api的統一管理。
整齊的api就像電路板一樣,即使再複雜也能很清晰整個線路。上面說了,我們會新建一個api.js,然後在這個檔案中存放我們所有的api介面。

首先我們在api.js中引入我們封裝的get和post方法

/**   
 * api介面統一管理
 */
import { get, post } from './http'
現在,例如我們有這樣一個介面,是一個post請求:

http://www.baiodu.com/api/v1/users/my_address/address_edit_before
我們可以在api.js中這樣封裝:

export const apiAddress = p => post('api/v1/users/my_address/address_edit_before', p);
我們定義了一個apiAddress方法,這個方法有一個引數p,p是我們請求介面時攜帶的引數物件。而後呼叫了我們封裝的post方法,post方法的第一個引數是我們的介面地址,第二個引數是apiAddress的p引數,即請求介面時攜帶的引數物件。最後通過export匯出apiAddress。

然後在我們的頁面中可以這樣呼叫我們的api介面:

import { apiAddress } from '@/request/api';// 匯入我們的api介面
export default {        
	name: 'Address',    
	created () {
		this.onLoad();
	},
	methods: {            
		// 獲取資料            
		onLoad() {
			// 呼叫api介面,並且提供了兩個引數                
			apiAddress({                    
				type: 0,                    
				sort: 1                
			}).then(res => {
				// 獲取資料成功後的其他操作
				………………                
			})            
		}        
	}
}
其他的api介面,就在pai.js中繼續往下面擴充套件就可以了。友情提示,為每個介面寫好註釋哦!!!

api介面管理的一個好處就是,我們把api統一集中起來,如果後期需要修改介面,我們就直接在api.js中找到對應的修改就好了,而不用去每一個頁面查詢我們的介面然後再修改會很麻煩。關鍵是,萬一修改的量比較大,就規格gg了。還有就是如果直接在我們的業務程式碼修改介面,一不小心還容易動到我們的業務程式碼造成不必要的麻煩。

好了,最後把完成的axios封裝程式碼奉上。

/**axios封裝
 * 請求攔截、相應攔截、錯誤統一處理
 */
import axios from 'axios';import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'

// 環境的切換
if (process.env.NODE_ENV == 'development') {    
	axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {    
	axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {    
	axios.defaults.baseURL = 'http://api.123dailu.com/';
}

// 請求超時時間
axios.defaults.timeout = 10000;

// post請求頭
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 請求攔截器
axios.interceptors.request.use(    
	config => {
		// 每次傳送請求之前判斷是否存在token,如果存在,則統一在http請求的header都加上token,不用每次請求都手動新增了
		// 即使本地存在token,也有可能token是過期的,所以在響應攔截器中要對返回狀態進行判斷
		const token = store.state.token;        
		token && (config.headers.Authorization = token);        
		return config;    
	},    
	error => {        
		return Promise.error(error);    
	})

// 響應攔截器
axios.interceptors.response.use(    
	response => {        
		if (response.status === 200) {            
			return Promise.resolve(response);        
		} else {            
			return Promise.reject(response);        
		}    
	},
	// 伺服器狀態碼不是200的情況    
	error => {        
		if (error.response.status) {            
			switch (error.response.status) {                
				// 401: 未登入                
				// 未登入則跳轉登入頁面,並攜帶當前頁面的路徑                
				// 在登入成功後返回當前頁面,這一步需要在登入頁操作。                
				case 401:                    
					router.replace({                        
						path: '/login',                        
						query: { redirect: router.currentRoute.fullPath } 
					});
					break;
				// 403 token過期                
				// 登入過期對使用者進行提示                
				// 清除本地token和清空vuex中token物件                
				// 跳轉登入頁面                
				case 403:                     
					Toast({                        
						message: '登入過期,請重新登入',                        
						duration: 1000,                        
						forbidClick: true                    
					});                    
					// 清除token                    
					localStorage.removeItem('token');                    
					store.commit('loginSuccess', null);                    
					// 跳轉登入頁面,並將要瀏覽的頁面fullPath傳過去,登入成功後跳轉需要存取的頁面
					setTimeout(() => {                        
						router.replace({                            
							path: '/login',                            
							query: { 
								redirect: router.currentRoute.fullPath 
							}                        
						});                    
					}, 1000);                    
					break; 
				// 404請求不存在                
				case 404:                    
					Toast({                        
						message: '網路請求不存在',                        
						duration: 1500,                        
						forbidClick: true                    
					});                    
				break;                
				// 其他錯誤,直接丟擲錯誤提示                
				default:                    
					Toast({                        
						message: error.response.data.message,                        
						duration: 1500,                        
						forbidClick: true                    
					});            
			}            
			return Promise.reject(error.response);        
		}       
	}
);
/** 
 * get方法,對應get請求 
 * @param {String} url [請求的url地址] 
 * @param {Object} params [請求時攜帶的引數] 
 */
export function get(url, params){    
	return new Promise((resolve, reject) =>{        
		axios.get(url, {            
			params: params        
		})        
		.then(res => {            
			resolve(res.data);        
		})        
		.catch(err => {            
			reject(err.data)        
		})    
	});
}
/** 
 * post方法,對應post請求 
 * @param {String} url [請求的url地址] 
 * @param {Object} params [請求時攜帶的引數] 
 */
export function post(url, params) {    
	return new Promise((resolve, reject) => {         
		axios.post(url, QS.stringify(params))        
		.then(res => {            
			resolve(res.data);        
		})        
		.catch(err => {            
			reject(err.data)        
		})    
	});
}
axios的封裝根據需求的不同而不同。
1.優化axios封裝,去掉之前的get和post

2.斷網情況處理

3.更加模組化的api管理

4.介面域名有多個的情況

5.api掛載到vue.prototype上省去引入的步驟

http.js中axios封裝的優化,先直接貼程式碼:

/**
 * axios封裝
 * 請求攔截、響應攔截、錯誤統一處理
 */
import axios from 'axios';
import router from '../router';
import store from '../store/index';
import { Toast } from 'vant';

/** 
 * 提示函數 
 * 禁止點選蒙層、顯示一秒後關閉
 */
const tip = msg => {    
	Toast({        
		message: msg,        
		duration: 1000,        
		forbidClick: true    
	});
}

/** 
 * 跳轉登入頁
 * 攜帶當前頁面路由,以期在登入頁面完成登入後返回當前頁面
 */
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);   
		}}

// 建立axios範例
var instance = axios.create({    timeout: 1000 * 12});
// 設定post請求頭
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
/** 
 * 請求攔截器 
 * 每次請求前,如果存在token則在請求頭中攜帶token 
 */ 
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中控制著一個全域性的斷網提示元件的顯示隱藏
			// 關於斷網元件中的重新整理重新獲取資料,會在斷網元件中說明
			if (!window.navigator.onLine) {
			   store.commit('changeNetwork', false);
			} else {
				return Promise.reject(error);
			}
		}
	});

export default instance;
這個axios和之前的大同小異,做了如下幾點改變:

1.去掉了之前get和post方法的封裝,通過建立一個axios範例然後export default方法匯出,這樣使用起來更靈活一些。

2.去掉了通過環境變數控制baseUrl的值。考慮到介面會有多個不同域名的情況,所以準備通過js變數來控制介面域名。這點具體在api裡會介紹。

3.增加了請求超時,即斷網狀態的處理。說下思路,當斷網時,通過更新vuex中network的狀態來控制斷網提示元件的顯示隱藏。斷網提示一般會有重新載入資料的操作,這步會在後面對應的地方介紹。

4.公用函數進行抽出,簡化程式碼,儘量保證單一職責原則。

下面說下api這塊,考慮到一下需求:

1.更加模組化

2.更方便多人開發,有效減少解決命名衝突

3.處理介面域名有多個情況

這裡這裡呢新建了一個api資料夾,裡面有一個index.js和一個base.js,以及多個根據模組劃分的介面js檔案。index.js是一個api的出口,base.js管理介面域名,其他js則用來管理各個模組的介面。

先放index.js程式碼:

/** 
 * api介面的統一出口
 */
// 文章模組介面
import article from '@/api/article';
// 其他模組的介面……

// 匯出介面
export default {    
	article,
	// ……
}
index.js是一個api介面的出口,這樣就可以把api介面根據功能劃分為多個模組,利於多人共同作業開發,比如一個人只負責一個模組的開發等,還能方便每個模組中介面的命名哦。

base.js:

/**
 * 介面域名的管理
 */
const base = {    
	sq: 'https://xxxx111111.com/api/v1',    
	bd: 'http://xxxxx22222.com/api'
}

export default base;
通過base.js來管理我們的介面域名,不管有多少個都可以通過這裡進行介面的定義。即使修改起來,也是很方便的。

最後就是介面模組的說明,例如上面的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;
1.通過直接引入我們封裝好的axios範例,然後定義介面、呼叫axios範例並返回,可以更靈活的使用axios,比如你可以對post請求時提交的資料進行一個qs序列化的處理等。

2.請求的設定更靈活,你可以針對某個需求進行一個不同的設定。關於設定的優先順序,axios檔案說的很清楚,這個順序是:在 lib/defaults.js 找到的庫的預設值,然後是範例的 defaults 屬性,最後是請求的 config 引數。後者將優先於前者。

3.restful風格的介面,也可以通過這種方式靈活的設定api介面地址。

最後,為了方便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的原型上
然後我們可以在頁面中這樣呼叫介面,eg:

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>
這是app.vue,這裡簡單演示一下斷網。在http.js中介紹了,我們會在斷網的時候,來更新vue中network的狀態,那麼這裡我們根據network的狀態來判斷是否需要載入這個斷網元件。斷網情況下,載入斷網元件,不載入對應頁面的元件。當點選重新整理的時候,我們通過跳轉refesh頁面然後立即返回的方式來實現重新獲取資料的操作。因此我們需要新建一個refresh.vue頁面,並在其beforeRouteEnter勾點中再返回當前頁面。


// refresh.vue
beforeRouteEnter (to, from, next) {
	next(vm => {            
		vm.$router.replace(from.fullPath)        
	})    
}

"""