前言
axios 是一個輕量的HTTP客戶端,它基於 XMLHttpRequest 服務來執行 HTTP 請求,支持豐富的配置,支持 Promise,支持瀏覽器端和 Node.js 端。自Vue2.0起,尤大大(Vue做者尤雨溪)宣佈取消對 vue-resource 的官方推薦,轉而推薦 axios。如今 axios 已經成爲大部分 Vue 開發者的首選。 (若是你還不熟悉 axios,能夠在這裏查看它的API)。html
axios 的API很友好,你徹底能夠很輕鬆地在項目中直接使用。不過隨着項目規模增大,若是每發起一次HTTP請求,就要把這些好比設置超時時間、設置請求頭、根據項目環境判斷使用哪一個請求地址、錯誤處理等等操做,都就地寫一遍,得瘋!這種重複勞動不只浪費時間,並且讓代碼變得冗餘不堪,難以維護。前端
爲了提升咱們的代碼質量,咱們應該在項目中二次封裝一下 axios
再使用。vue
那麼,怎麼封裝 axios
呢?ios
原來的樣子
封裝前,先來看下,不封裝的狀況下,一個實際項目中axios請求的樣子。大概是長這樣:git
axios('http://localhost:3000/data', { method: 'GET', timeout: 1000, withCredentials: true, headers: { 'Content-Type': 'application/json', Authorization: 'xxx', }, transformRequest: [function (data, headers) { return data; }], // 其餘請求配置... }) .then((data) => { // todo: 真正業務邏輯代碼 console.log(data); }, (err) => { if (err.response.status === 401) { // handle authorization error } if (err.response.status === 403) { // handle server forbidden error } // 其餘錯誤處理..... console.log(err); });
能夠看到在這段代碼中,頁面代碼邏輯只在第15行處,上方的一大塊請求配置代碼和下方一大塊響應錯誤處理代碼,幾乎跟頁面功能沒有關係,並且每一個請求中這些內容都差很少,甚至有的部分徹底同樣。想象一下,每發一次請求都來這麼一下,十幾個請求一寫,會是什麼盛況?github
封裝步驟
封裝的本質就是在待封裝的內容外面添加各類東西,而後把它們做爲一個新的總體呈現給使用者,以達到擴展和易用的目的。npm
封裝axios
要作的事情,就是把全部HTTP請求共用的配置,事先都在axios上配置好,預留好必要的參數和接口,而後把它做爲新的axios返回。json
接下來咱們藉助一個demo實現一個具備良好擴展性的axios
封裝。axios
demo目錄結構以下(由Vue-cli 3.0 生成):api
|--public/ |--mock/ | |--db.json # 我新建的接口模擬數據 |--src/ | |--assets/ | |--components/ | |--router/ | |--store/ | |--views/ | |--Home.Vue | |--App.vue | |--main.js | |--theme.styl |--package.json |...
封裝目標
我但願在 Home 頁,發起 axios 請求時就像調用一個只有少許參數的方法同樣簡單,這樣我就能夠專一業務代碼了。
1. 將 axios 封裝到一個獨立的文件
- 在src下建立 utils/http.js 文件
cd src mkdir utils touch http.js
- 引入 axios
// src/utils/http.js import axios from 'axios';
- 建立一個類 你也能夠用函數來封裝,我只是以爲類更語義化而已。
//src/utils/http.js //... class NewAxios { }
- 給不一樣環境配置不一樣請求地址 根據
process.env.NODE_ENV
配置不一樣的baseURL
,使項目只需執行相應打包命令,就能夠在不一樣環境中自動切換請求主機地址。
// src/utils/http.js //... const getBaseUrl = (env) => { let base = { production: '/', development: 'http://localhost:3000', test: 'http://localhost:3001', }[env]; if (!base) { base = '/'; } return base; }; class NewAxios { constructor() { this.baseURL = getBaseUrl(process.env.NODE_ENV); } }
- 配置超時時間 timeout屬性,我通常設置10秒。
// src/utils/http.js //... class NewAxios { constructor() { //... this.timeout = 10000; } }
- 配置容許攜帶憑證 widthCredentials屬性設爲true。
// src/utils/http.js //... class NewAxios { constructor() { //... this.withCredentials = true; } }
- 給這個類建立實例上的方法request 在
request
方法裏,建立新的axios實例,接收請求配置參數,處理參數,添加配置,返回axios實例的請求結果(一個promise對象)。 你也能夠不建立,直接使用默認導出的axios實例,而後把全部配置都放到它上面,不過這樣一來整個項目就會共用一個axios實例。雖然大部分項目下這樣夠用沒問題,可是有的項目中不一樣服務地址的請求和響應結構可能徹底不一樣,這個時候共用一個實例就沒辦法支持了。因此爲了封裝能夠更通用,更具靈活性,我會使用axios的create方法,使每次發請求都是新的axios實例。
// src/utils/http.js //... class NewAxios { //... request(options) { // 每次請求都會建立新的axios實例。 const instance = axios.create(); const config = { // 將用戶傳過來的參數與公共配置合併。 ...options, baseURL: this.baseURL, timeout: this.timeout, withCredentials: this.withCredentials, }; // 配置攔截器,支持根據不一樣url配置不一樣的攔截器。 this.setInterceptors(instance, options.url); return instance(config); // 返回axios實例的執行結果 } }
由於攔截器配置內容比較多,因此封裝成一個內部函數了。
- 配置請求攔截器 在發送請求前對請求參數作的全部修改都在這裏統一配置。好比統一添加token憑證、統一設置語言、統一設置內容類型、指定數據格式等等。作完後記得返回這個配置,不然整個請求不會進行。 我這裏就配置一個token。
// src/utils/http.js //... class NewAxios { //... // 這裏的url可供你針對須要特殊處理的接口路徑設置不一樣攔截器。 setInterceptors = (instance, url) => { instance.interceptors.request.use((config) => { // 請求攔截器 // 配置token config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || ''; return config; }, err => Promise.reject(err)); } //... }
- 配置響應攔截器 在請求的
then
或catch
處理前對響應數據進行一輪預先處理。好比過濾響應數據,更多的,是在這裏對各類響應錯誤碼進行統一錯誤處理,還有斷網處理等等。 我這裏就判斷一下403和斷網。
// src/utils/http.js //... class NewAxios { //... setInterceptors = (instance, url) => { //... instance.interceptors.response.use((response) => { // 響應攔截器 // todo: 想根據業務須要,對響應結果預先處理的,都放在這裏 console.log(); return response; }, (err) => { if (err.response) { // 響應錯誤碼處理 switch (err.response.status) { case '403': // todo: handler server forbidden error break; // todo: handler other status code default: break; } return Promise.reject(err.response); } if (!window.navigator.online) { // 斷網處理 // todo: jump to offline page return -1; } return Promise.reject(err); }); } //... }
另外,在攔截器裏,還適合放置loading等緩衝效果:在請求攔截器裏顯示loading,在響應攔截器裏移除loading。這樣全部請求就都有了一個統一的loading效果。
- 默認導出新的實例
// src/utils/http.js //... export default new NewAxios();
最後完整的代碼以下:
// src/utils/http.js import axios from 'axios'; const getBaseUrl = (env) => { let base = { production: '/', development: 'http://localhost:3000', test: 'http://localhost:3001', }[env]; if (!base) { base = '/'; } return base; }; class NewAxios { constructor() { this.baseURL = getBaseUrl(process.env.NODE_ENV); this.timeout = 10000; this.withCredentials = true; } setInterceptors = (instance, url) => { instance.interceptors.request.use((config) => { // 在這裏添加loading // 配置token config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || ''; return config; }, err => Promise.reject(err)); instance.interceptors.response.use((response) => { // 在這裏移除loading // todo: 想根據業務須要,對響應結果預先處理的,都放在這裏 return response; }, (err) => { if (err.response) { // 響應錯誤碼處理 switch (err.response.status) { case '403': // todo: handler server forbidden error break; // todo: handler other status code default: break; } return Promise.reject(err.response); } if (!window.navigator.online) { // 斷網處理 // todo: jump to offline page return -1; } return Promise.reject(err); }); } request(options) { // 每次請求都會建立新的axios實例。 const instance = axios.create(); const config = { // 將用戶傳過來的參數與公共配置合併。 ...options, baseURL: this.baseURL, timeout: this.timeout, withCredentials: this.withCredentials, }; // 配置攔截器,支持根據不一樣url配置不一樣的攔截器。 this.setInterceptors(instance, options.url); return instance(config); // 返回axios實例的執行結果 } } export default new NewAxios();
如今 axios
封裝算是完成了80%。咱們還須要再進一步把axios和接口結合再封裝一層,才能達到我在一開始定的封裝目標。
2. 使用新的 axios 封裝API
- 在 src 目錄下新建
api
文件夾。把全部涉及HTTP請求的接口統一集中到這個目錄來管理。 - 新建
home.js
。咱們須要把接口根據必定規則分好類,一類接口對應一個js文件。這個分類能夠是按頁面來劃分,或者按模塊等等。爲了演示更直觀,我這裏就按頁面來劃分了。實際根據本身的需求來定。 - 使用新的 axios 封裝API(固定url的值,合併用戶傳過來的參數),而後命名導出這些函數。
// src/api/home.js import axios from '@/utils/http'; export const fetchData = options => axios.request({ ...options, url: '/data', }); export default {};
- 在 api 目錄下新建
index.js
,把其餘文件的接口都在這個文件裏彙總導出。
// src/api/index.js export * from './home';
這層封裝將咱們的新的axios封裝到了更簡潔更語義化的接口方法中。
如今咱們的目錄結構長這樣:
|--public/ |--mock/ | |--db.json # 接口模擬數據 |--src/ | |--api/ # 全部的接口都集中在這個目錄下 | |--home.js # Home頁面裏涉及到的接口封裝在這裏 | |--index.js # 項目中全部接口調用的入口 | |--assets/ | |--components/ | |--router/ | |--store/ | |--utils/ | |--http.js # axios封裝在這裏 | |--views/ | |--Home.Vue | |--App.vue | |--main.js | |--theme.styl |--package.json |...
使用封裝後的axios
如今咱們要發HTTP請求時,只需引入 api
下的 index.js
文件就能夠調用任何接口了,而且用的是封裝後的 axios
。
// src/views/Home.vue <template> <div class="home"> <h1>This is home page</h1> </div> </template> <script> // @ is an alias to /src import { fetchData } from '@/api/index'; export default { name: 'home', mounted() { fetchData() // axios請求在這裏 .then((data) => { console.log(data); }) .catch((err) => { console.log(err); }); }, }; </script>
axios請求被封裝在fetchData
函數裏,頁面請求壓根不須要出現任何axios API
,悄無聲息地發起請求獲取響應,就像在調用一個簡單的 Promise
函數同樣輕鬆。而且在頁面中只需專一處理業務功能,不用被其餘事物干擾。
運行
運行 npm run serve
啓動項目,執行 npm run mock
啓動服務mock接口。
如今打開 localhost:8080
能夠看到home頁面。打開瀏覽器控制檯,能夠看到打印的請求響應結果:
簡潔,優雅。
總結
- 封裝思想是前端技術中頗有用的思想,簡單的axios及接口封裝,就可讓咱們能夠領略到它的魅力。
- 封裝
axios
沒有一個絕對的標準,只要你的封裝能夠知足你的項目需求,而且用起來方便,那就是一個好的封裝方案。 - BTW:以上封裝給你們提供了一個封裝好的axios和api框架,通過以上過程封裝好的
axios
,能夠不侷限於 Vue,React 項目一樣能夠拿去使用,它適用任何前端項目。
本文的代碼能夠在這裏獲取:https://github.com/yc111/wrap-axios
歡迎交流~
歡迎轉載,轉載請註明出處: https://champyin.com/2019/12/23/%E5%B0%81%E8%A3%85axios/
原文出處:https://www.cnblogs.com/champyin/p/12115617.html