ES6 + 實(shí)戰(zhàn) 2 - 封裝請(qǐng)求
1. 前言
在學(xué)習(xí) Promise 相關(guān)小節(jié)時(shí)我們已經(jīng)了解了 Promise 的基本用法和 Promise A + 規(guī)范,那么在實(shí)際項(xiàng)目中我們應(yīng)該怎么去使用 Promise 來提高我們的效率,并且可以通過 Promise 去封裝一些異步方法,讓我們?cè)谑褂眠^程中更加得心應(yīng)手。
本節(jié)我們將模擬一個(gè)真實(shí)的生產(chǎn)環(huán)境來對(duì)前端開發(fā)中最常見也是最重要的數(shù)據(jù)請(qǐng)求進(jìn)行封裝。通過使用封裝 Promise 請(qǐng)求來學(xué)習(xí) Promise 在實(shí)際項(xiàng)目當(dāng)中是如何使用的。
2. 環(huán)境搭建
工欲善其事,必先利其器,在我們進(jìn)入本節(jié)的學(xué)習(xí)前,我們需要先搭建我們的開發(fā)環(huán)境,在實(shí)際的項(xiàng)目中也是必須的。本節(jié)使用的是 Vue 腳手架生成的項(xiàng)目,不了解 Vue 的同學(xué)可以先去學(xué)習(xí)一下。在 vue.config.js 配置中,對(duì) ajax 請(qǐng)求進(jìn)行了 mock 操作,mock 的邏輯在 mock.config.js 文件中,mock 的結(jié)果在 mock 文件夾下對(duì)應(yīng)的 json。
這樣的配置在本節(jié)中就可以基本模擬真實(shí)的數(shù)據(jù)請(qǐng)求過程了,本節(jié)的源碼在 GitHub 上。
3. 封裝 ajax 請(qǐng)求
ajax 是前端用于發(fā)送接口請(qǐng)求的技術(shù),它是異步的,需要等待結(jié)果返回后執(zhí)行
在發(fā)送 ajax 請(qǐng)求時(shí),我們可能會(huì)這樣去寫。
ajax({
url: '',
method: '',
data: {},
params: {},
success: function (res) {},
error: function (err) {}
})
- url: 接口請(qǐng)求地址;
- method: 接口請(qǐng)求方法,如:get、post 等;
- data: 請(qǐng)求時(shí)使用 body 傳輸?shù)臄?shù)據(jù),一般用于 post 請(qǐng)求中;
- params: 請(qǐng)求時(shí)使用 url 傳遞的數(shù)據(jù),一般用于 get 請(qǐng)求中;
- success: 接口請(qǐng)求成功時(shí)的回調(diào),參數(shù)為接口成功的返回值;
- error: 接口請(qǐng)求失敗時(shí)的回調(diào),參數(shù)為拋出異常時(shí)的調(diào)用棧等信息。
XMLHttpRequest 是瀏覽器提供的對(duì)象,用于進(jìn)行后臺(tái)與服務(wù)端的數(shù)據(jù)進(jìn)行交互
實(shí)現(xiàn) ajax
function ajax(options) {
const { url, method, data, params, success, error } = options;
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
// readyState為4的時(shí)候已接收完畢
if (xhr.readyState === 4) {
// 狀態(tài)碼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 請(qǐng)求
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 請(qǐng)求
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 進(jìn)行封裝
function ajax(url, method, params) {
return new Promise((resolve, reject) => {
// 創(chuàng)建XMLHttpRequest對(duì)象
const xhr = new XMLHttpRequest();
// 狀態(tài)改變時(shí)的回調(diào)
xhr.onreadystatechange = function () {
// readyState為4的時(shí)候已接收完畢
if (xhr.readyState === 4) {
// 狀態(tài)碼200表示成功
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.status);
}
}
};
// ...
});
}
4. axios 庫封裝
在真實(shí)的項(xiàng)目中會(huì)經(jīng)常使用到 axios 這樣的 ajax 請(qǐng)求的庫,雖然可以直接使用,但是往往業(yè)務(wù)中會(huì)有很多接口請(qǐng)求的地方,而這么多的請(qǐng)求有些固定不變的,每個(gè)接口在請(qǐng)求時(shí)都需要,如:token,baseURL,timeout 等等,針對(duì)這樣的場景,我們可以對(duì) axios 庫進(jìn)行二次業(yè)務(wù)封裝。對(duì)于接口不同的返回結(jié)果我們希望有一個(gè)全局的提示框,這里我們使用 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. 小結(jié)
本節(jié)我們通過真實(shí)的業(yè)務(wù)場景觸發(fā),對(duì)原生的 ajax 請(qǐng)求做了 promise 封裝,最后我們對(duì)真實(shí)的業(yè)務(wù)場景使用 axios 對(duì)業(yè)務(wù)二次封裝,這樣更好地復(fù)用業(yè)務(wù)邏輯,統(tǒng)一增加不同返回結(jié)果的提示。