ES6 + 實戰 2 - 封裝請求
1. 前言
在學習 Promise 相關小節時我們已經了解了 Promise 的基本用法和 Promise A + 規范,那么在實際項目中我們應該怎么去使用 Promise 來提高我們的效率,并且可以通過 Promise 去封裝一些異步方法,讓我們在使用過程中更加得心應手。
本節我們將模擬一個真實的生產環境來對前端開發中最常見也是最重要的數據請求進行封裝。通過使用封裝 Promise 請求來學習 Promise 在實際項目當中是如何使用的。
2. 環境搭建
工欲善其事,必先利其器,在我們進入本節的學習前,我們需要先搭建我們的開發環境,在實際的項目中也是必須的。本節使用的是 Vue 腳手架生成的項目,不了解 Vue 的同學可以先去學習一下。在 vue.config.js 配置中,對 ajax 請求進行了 mock 操作,mock 的邏輯在 mock.config.js 文件中,mock 的結果在 mock 文件夾下對應的 json。
這樣的配置在本節中就可以基本模擬真實的數據請求過程了,本節的源碼在 GitHub 上。
3. 封裝 ajax 請求
ajax 是前端用于發送接口請求的技術,它是異步的,需要等待結果返回后執行
在發送 ajax 請求時,我們可能會這樣去寫。
ajax({
url: '',
method: '',
data: {},
params: {},
success: function (res) {},
error: function (err) {}
})
- url: 接口請求地址;
- method: 接口請求方法,如:get、post 等;
- data: 請求時使用 body 傳輸的數據,一般用于 post 請求中;
- params: 請求時使用 url 傳遞的數據,一般用于 get 請求中;
- success: 接口請求成功時的回調,參數為接口成功的返回值;
- error: 接口請求失敗時的回調,參數為拋出異常時的調用棧等信息。
XMLHttpRequest 是瀏覽器提供的對象,用于進行后臺與服務端的數據進行交互
實現 ajax
function ajax(options) {
const { url, method, data, params, success, error } = options;
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
// readyState為4的時候已接收完畢
if (xhr.readyState === 4) {
// 狀態碼200表示成功
if (xhr.status === 200) {
console.log(xhr.responseText);
success.call(this, xhr.responseText);
} else {
console.log(xhr.status);
error.call(this, xhr.status)
}
}
};
// get 請求
if (method === 'get' || method === 'GET') {
if (typeof params === 'object') {
// params拆解成字符串
params = Object.keys(params).map(function (key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
}
url = params ? `${url}?${params}` : url;
xhr.open(method, url, true);
xhr.send();
}
// post 請求
if (method === 'post' || method === 'POST') {
xhr.open(method, url, true);
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");
xhr.send(JSON.stringify(params));
}
}
使用 promise 進行封裝
function ajax(url, method, params) {
return new Promise((resolve, reject) => {
// 創建XMLHttpRequest對象
const xhr = new XMLHttpRequest();
// 狀態改變時的回調
xhr.onreadystatechange = function () {
// readyState為4的時候已接收完畢
if (xhr.readyState === 4) {
// 狀態碼200表示成功
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.status);
}
}
};
// ...
});
}
4. axios 庫封裝
在真實的項目中會經常使用到 axios 這樣的 ajax 請求的庫,雖然可以直接使用,但是往往業務中會有很多接口請求的地方,而這么多的請求有些固定不變的,每個接口在請求時都需要,如:token,baseURL,timeout 等等,針對這樣的場景,我們可以對 axios 庫進行二次業務封裝。對于接口不同的返回結果我們希望有一個全局的提示框,這里我們使用 element-ui 組件庫搭配使用。封裝后的代碼如下:
import axios from 'axios';
import { baseURL } from '@/config'
class Http {
constructor(baseUrl) {
this.baseURL = baseURL;
this.timeout = 3000;
}
setInterceptor(instance) {
instance.interceptors.request.use(config => {
return config;
});
instance.interceptors.response.use(res => {
if (res.status == 200) {
return Promise.resolve(res.data);
} else {
return Promise.reject(res);
}
}, err => {
return Promise.reject(err);
});
}
mergeOptions(options) {
return {
baseURL: this.baseURL,
timeout: this.timeout,
...options
}
}
request(options) {
const instance = axios.create();
const opts = this.mergeOptions(options);
this.setInterceptor(instance);
return instance(opts);
}
get(url, config = {}) {
return this.request({
method: 'get',
url: url,
...config
})
}
post(url, data) {
return this.request({
method: 'post',
url,
data
})
}
}
export default new Http;
5. 小結
本節我們通過真實的業務場景觸發,對原生的 ajax 請求做了 promise 封裝,最后我們對真實的業務場景使用 axios 對業務二次封裝,這樣更好地復用業務邏輯,統一增加不同返回結果的提示。