1. 前言
在学习 Promise 相关小节时我们已经了解了 Promise 的基本用法和 Promise A+规范,那么在实际项目中我们应该怎么去使用 Promise 来提高我们的效率,并且可以通过 Promise 去封装一些异步方法,让我们在使用过程中更加得心应手。
本文我们将模拟一个真实的生产环境来对前端开发中最常见也是最重要的数据请求进行封装。通过使用封装 Promise 请求来学习 Promise 在实际项目当中是如何使用的。
2. 环境搭建
工欲善其事,必先利其器,在我们进入本节的学习前,我们需要先搭建我们的开发环境,在实际的项目中也是必须的。本节使用的是 Vue 脚手架生成的项目,这里默认大家对 Vue 有了解。在 vue.config.js 配置中,对 ajax 请求进行了 mock 操作,mock 的逻辑在 mock.config.js 文件中,mock 的结果在 mock 文件夹下对应的 json。
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
是浏览器提供的对象,用于进行后台与服务端的数据进行交互
3.1 实现 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 对业务二次封装,这样更好地复用业务逻辑,统一增加不同返回结果的提示。